From 597482080ac0a255b4f3c9a6eead2017c9c7d854 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Fri, 15 May 2026 15:59:57 +0100 Subject: [PATCH 1/8] remove self dependency in raphtory --- Cargo.toml | 2 +- raphtory-benchmark/Cargo.toml | 1 + raphtory/Cargo.toml | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4baf275165..201bb3b6aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,7 +73,7 @@ optd-core = { version = "0.18.0", path = "optd/optd/core" } async-graphql = { version = "7.2.1", features = ["dynamic-schema"] } bincode = { version = "2", features = ["serde"] } async-graphql-poem = "7.2.1" -dynamic-graphql = { git = "https://github.com/miratepuffin/dynamic-graphql", branch = "add-arg-descriptions" } +dynamic-graphql = "0.10.2" derive_more = "2.1.1" tikv-jemallocator = "0.6.1" reqwest = { version = "0.12.28", default-features = false, features = [ diff --git a/raphtory-benchmark/Cargo.toml b/raphtory-benchmark/Cargo.toml index 0c288e3ec1..5e3ffa56c2 100644 --- a/raphtory-benchmark/Cargo.toml +++ b/raphtory-benchmark/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "raphtory-benchmark" +publish = false version.workspace = true edition = "2021" diff --git a/raphtory/Cargo.toml b/raphtory/Cargo.toml index 1629456088..88c3ac424d 100644 --- a/raphtory/Cargo.toml +++ b/raphtory/Cargo.toml @@ -116,7 +116,9 @@ tokio = { workspace = true } # for vector testing dotenv = { workspace = true } # for vector testing streaming-stats = { workspace = true } indoc = { workspace = true } -raphtory = { workspace = true, features = ["test-utils"] } # enable test-utils for integration tests +proptest.workspace = true +proptest-derive.workspace = true +# raphtory = { workspace = true, features = ["test-utils"] } [target.'cfg(target_os = "macos")'.dependencies] tikv-jemallocator = "0.6.1" @@ -125,7 +127,7 @@ tikv-jemallocator = "0.6.1" prost-build = { workspace = true, optional = true } [features] -default = [] +default = ["test-utils"] # we can't depend on ourselves but we want to share test-utils # enables progress bars progress = ["dep:kdam"] From e44c06b5d3666cc0b757773f31e070e2efcf104c Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Mon, 18 May 2026 11:35:44 +0100 Subject: [PATCH 2/8] trying to fix the features issues with test-utils --- Cargo.lock | 11 +- raphtory-api/src/core/entities/mod.rs | 1 - raphtory/tests/algo_tests/centrality.rs | 571 +- .../tests/algo_tests/community_detection.rs | 395 +- raphtory/tests/algo_tests/components.rs | 1649 +- raphtory/tests/algo_tests/cores.rs | 2 +- raphtory/tests/algo_tests/embeddings.rs | 2 +- raphtory/tests/algo_tests/metrics.rs | 12 +- raphtory/tests/algo_tests/motifs.rs | 12 +- raphtory/tests/algo_tests/pathing.rs | 6 +- raphtory/tests/cached_view.rs | 661 +- raphtory/tests/db_tests.rs | 6872 ++--- raphtory/tests/df_loaders.rs | 5 +- raphtory/tests/edge_property_filter.rs | 693 +- .../tests/exploded_edge_property_filter.rs | 1229 +- raphtory/tests/node_property_filter.rs | 901 +- raphtory/tests/node_test.rs | 831 +- raphtory/tests/proto_test.rs | 3 +- raphtory/tests/serialise_test.rs | 3 +- raphtory/tests/subgraph_tests.rs | 1047 +- raphtory/tests/test_deletions.rs | 2169 +- raphtory/tests/test_edge.rs | 359 +- raphtory/tests/test_edge_view.rs | 303 +- raphtory/tests/test_exploded_edges.rs | 1101 +- raphtory/tests/test_filters.rs | 23325 ++++++++-------- raphtory/tests/test_layers.rs | 1456 +- raphtory/tests/test_materialize.rs | 365 +- .../tests_node_type_filtered_subgraph.rs | 1275 +- raphtory/tests/time_tests.rs | 621 +- raphtory/tests/valid_graph.rs | 754 +- raphtory/tests/views_test.rs | 8880 +++--- 31 files changed, 27812 insertions(+), 27702 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a5ce66f44..afb29f7572 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2651,8 +2651,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "dynamic-graphql" -version = "0.10.1" -source = "git+https://github.com/miratepuffin/dynamic-graphql?branch=add-arg-descriptions#69a07c5fe3c16b4baf76f676c96cde5865cae1de" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d58055cecef1736f42bee8819059054e18239cda3d5de56b0c8836c2a2322b" dependencies = [ "async-graphql", "dynamic-graphql-derive", @@ -2661,8 +2662,9 @@ dependencies = [ [[package]] name = "dynamic-graphql-derive" -version = "0.10.1" -source = "git+https://github.com/miratepuffin/dynamic-graphql?branch=add-arg-descriptions#69a07c5fe3c16b4baf76f676c96cde5865cae1de" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1e2aa3d6affef7ce88263e83c0110b11f57409cf25aa81689bcc5f023a602e" dependencies = [ "Inflector", "darling 0.20.11", @@ -6397,7 +6399,6 @@ dependencies = [ "quad-rand", "rand 0.9.4", "rand_distr 0.5.1", - "raphtory", "raphtory-api", "raphtory-core", "raphtory-itertools", diff --git a/raphtory-api/src/core/entities/mod.rs b/raphtory-api/src/core/entities/mod.rs index ab7f914cbb..2959371455 100644 --- a/raphtory-api/src/core/entities/mod.rs +++ b/raphtory-api/src/core/entities/mod.rs @@ -705,7 +705,6 @@ impl From for LayerIds { mod tests { use crate::core::entities::{LayerId, EID, MAX_EID}; use proptest::{prop_assert, prop_assert_eq, proptest}; - use std::char::MAX; #[test] fn test_elid_layer() { diff --git a/raphtory/tests/algo_tests/centrality.rs b/raphtory/tests/algo_tests/centrality.rs index b836fd2753..2e04370593 100644 --- a/raphtory/tests/algo_tests/centrality.rs +++ b/raphtory/tests/algo_tests/centrality.rs @@ -1,311 +1,314 @@ -use std::collections::HashMap; - -use crate::algo_tests::assert_eq_hashmaps_approx; -use itertools::Itertools; -use raphtory::{ - algorithms::centrality::{ - betweenness::betweenness_centrality, degree_centrality::degree_centrality, hits::hits, - pagerank::unweighted_page_rank, - }, - prelude::*, - test_storage, -}; - -#[test] -fn test_betweenness_centrality() { - let graph = Graph::new(); - let vs = vec![ - (1, 2), - (1, 3), - (1, 4), - (2, 3), - (2, 4), - (2, 5), - (3, 4), - (3, 5), - (3, 6), - (4, 3), - (4, 2), - (4, 4), - ]; - for (src, dst) in &vs { - graph.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let mut expected: HashMap = HashMap::new(); - expected.insert("1".to_string(), 0.0); - expected.insert("2".to_string(), 1.0); - expected.insert("3".to_string(), 4.0); - expected.insert("4".to_string(), 1.0); - expected.insert("5".to_string(), 0.0); - expected.insert("6".to_string(), 0.0); - - let res = betweenness_centrality(graph, None, false) - .to_hashmap(|value| value.betweenness_centrality); - assert_eq!(res, expected); - - let mut expected: HashMap = HashMap::new(); - expected.insert("1".to_string(), 0.0); - expected.insert("2".to_string(), 0.05); - expected.insert("3".to_string(), 0.2); - expected.insert("4".to_string(), 0.05); - expected.insert("5".to_string(), 0.0); - expected.insert("6".to_string(), 0.0); - let res = betweenness_centrality(graph, None, true) - .to_hashmap(|value| value.betweenness_centrality); - assert_eq!(res, expected); - }); -} +#[cfg(all(test, feature = "test-utils"))] +mod test { + use std::collections::HashMap; + + use crate::algo_tests::assert_eq_hashmaps_approx; + use itertools::Itertools; + use raphtory::{ + algorithms::centrality::{ + betweenness::betweenness_centrality, degree_centrality::degree_centrality, hits::hits, + pagerank::unweighted_page_rank, + }, + prelude::*, + test_storage, + }; + + #[test] + fn test_betweenness_centrality() { + let graph = Graph::new(); + let vs = vec![ + (1, 2), + (1, 3), + (1, 4), + (2, 3), + (2, 4), + (2, 5), + (3, 4), + (3, 5), + (3, 6), + (4, 3), + (4, 2), + (4, 4), + ]; + for (src, dst) in &vs { + graph.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); + } -#[test] -fn test_degree_centrality() { - let graph = Graph::new(); - let vs = vec![(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]; - for (src, dst) in &vs { - graph.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + let mut expected: HashMap = HashMap::new(); + expected.insert("1".to_string(), 0.0); + expected.insert("2".to_string(), 1.0); + expected.insert("3".to_string(), 4.0); + expected.insert("4".to_string(), 1.0); + expected.insert("5".to_string(), 0.0); + expected.insert("6".to_string(), 0.0); + + let res = betweenness_centrality(graph, None, false) + .to_hashmap(|value| value.betweenness_centrality); + assert_eq!(res, expected); + + let mut expected: HashMap = HashMap::new(); + expected.insert("1".to_string(), 0.0); + expected.insert("2".to_string(), 0.05); + expected.insert("3".to_string(), 0.2); + expected.insert("4".to_string(), 0.05); + expected.insert("5".to_string(), 0.0); + expected.insert("6".to_string(), 0.0); + let res = betweenness_centrality(graph, None, true) + .to_hashmap(|value| value.betweenness_centrality); + assert_eq!(res, expected); + }); } - test_storage!(&graph, |graph| { - let mut expected: HashMap = HashMap::new(); - expected.insert("1".to_string(), 1.0); - expected.insert("2".to_string(), 1.0); - expected.insert("3".to_string(), 2.0 / 3.0); - expected.insert("4".to_string(), 2.0 / 3.0); - - let res = degree_centrality(graph).to_hashmap(|value| value.score); - assert_eq!(res, expected); - }); -} -#[test] -fn test_hits() { - let edges = vec![ - (1, 4), - (2, 3), - (2, 5), - (3, 1), - (4, 2), - (4, 3), - (5, 2), - (5, 3), - (5, 4), - (5, 6), - (6, 3), - (6, 8), - (7, 1), - (7, 3), - (8, 1), - ]; - let graph = Graph::new(); - - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + #[test] + fn test_degree_centrality() { + let graph = Graph::new(); + let vs = vec![(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]; + for (src, dst) in &vs { + graph.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let mut expected: HashMap = HashMap::new(); + expected.insert("1".to_string(), 1.0); + expected.insert("2".to_string(), 1.0); + expected.insert("3".to_string(), 2.0 / 3.0); + expected.insert("4".to_string(), 2.0 / 3.0); + + let res = degree_centrality(graph).to_hashmap(|value| value.score); + assert_eq!(res, expected); + }); } - test_storage!(&graph, |graph| { - let results = hits(graph, 20, None); - - let expected = HashMap::from([ - ("1".to_string(), (0.0431365, 0.096625775)), - ("2".to_string(), (0.14359662, 0.18366566)), - ("3".to_string(), (0.030866561, 0.36886504)), - ("4".to_string(), (0.1865414, 0.12442485)), - ("5".to_string(), (0.26667944, 0.05943252)), - ("6".to_string(), (0.14359662, 0.10755368)), - ("7".to_string(), (0.15471625, 0.0)), - ("8".to_string(), (0.030866561, 0.05943252)), - ]); - - assert_eq!(results.len(), 8); - for (node, value) in results.iter() { - let expected_value = expected.get(&node.name()).unwrap(); - assert!( - (value.hub_score - expected_value.0).abs() < 1e-6, - "mismatched hub score for node {}, expected {}, actual {}", - node.name(), - expected_value.0, - value.hub_score - ); - assert!( - (value.auth_score - expected_value.1).abs() < 1e-6, - "mismatched authority score for node {}, expected {}, actual {}", - node.name(), - expected_value.1, - value.auth_score - ); + + #[test] + fn test_hits() { + let edges = vec![ + (1, 4), + (2, 3), + (2, 5), + (3, 1), + (4, 2), + (4, 3), + (5, 2), + (5, 3), + (5, 4), + (5, 6), + (6, 3), + (6, 8), + (7, 1), + (7, 3), + (8, 1), + ]; + let graph = Graph::new(); + + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); } - }) -} + test_storage!(&graph, |graph| { + let results = hits(graph, 20, None); + + let expected = HashMap::from([ + ("1".to_string(), (0.0431365, 0.096625775)), + ("2".to_string(), (0.14359662, 0.18366566)), + ("3".to_string(), (0.030866561, 0.36886504)), + ("4".to_string(), (0.1865414, 0.12442485)), + ("5".to_string(), (0.26667944, 0.05943252)), + ("6".to_string(), (0.14359662, 0.10755368)), + ("7".to_string(), (0.15471625, 0.0)), + ("8".to_string(), (0.030866561, 0.05943252)), + ]); + + assert_eq!(results.len(), 8); + for (node, value) in results.iter() { + let expected_value = expected.get(&node.name()).unwrap(); + assert!( + (value.hub_score - expected_value.0).abs() < 1e-6, + "mismatched hub score for node {}, expected {}, actual {}", + node.name(), + expected_value.0, + value.hub_score + ); + assert!( + (value.auth_score - expected_value.1).abs() < 1e-6, + "mismatched authority score for node {}, expected {}, actual {}", + node.name(), + expected_value.1, + value.auth_score + ); + } + }) + } -#[test] -fn test_page_rank() { - let graph = Graph::new(); + #[test] + fn test_page_rank() { + let graph = Graph::new(); - let edges = vec![(1, 2), (1, 4), (2, 3), (3, 1), (4, 1)]; + let edges = vec![(1, 2), (1, 4), (2, 3), (3, 1), (4, 1)]; - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let expected: HashMap = HashMap::from([ + ("1".to_string(), 0.38694), + ("2".to_string(), 0.20195), + ("3".to_string(), 0.20916), + ("4".to_string(), 0.20195), + ]); + let results = unweighted_page_rank(graph, Some(1000), Some(1), None, true, None) + .to_hashmap(|value| value.score); + assert_eq_hashmaps_approx(&results, &expected, 1e-5); + }); } - test_storage!(&graph, |graph| { - let expected: HashMap = HashMap::from([ - ("1".to_string(), 0.38694), - ("2".to_string(), 0.20195), - ("3".to_string(), 0.20916), - ("4".to_string(), 0.20195), - ]); - let results = unweighted_page_rank(graph, Some(1000), Some(1), None, true, None) - .to_hashmap(|value| value.score); - assert_eq_hashmaps_approx(&results, &expected, 1e-5); - }); -} + #[test] + fn motif_page_rank() { + let edges = vec![ + (1, 2, 1), + (1, 3, 2), + (1, 4, 3), + (3, 1, 4), + (3, 4, 5), + (3, 5, 6), + (4, 5, 7), + (5, 6, 8), + (5, 8, 9), + (7, 5, 10), + (8, 5, 11), + (1, 9, 12), + (9, 1, 13), + (6, 3, 14), + (4, 8, 15), + (8, 3, 16), + (5, 10, 17), + (10, 5, 18), + (10, 8, 19), + (1, 11, 20), + (11, 1, 21), + (9, 11, 22), + (11, 9, 23), + ]; + + let graph = Graph::new(); + + for (src, dst, t) in edges { + graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } -#[test] -fn motif_page_rank() { - let edges = vec![ - (1, 2, 1), - (1, 3, 2), - (1, 4, 3), - (3, 1, 4), - (3, 4, 5), - (3, 5, 6), - (4, 5, 7), - (5, 6, 8), - (5, 8, 9), - (7, 5, 10), - (8, 5, 11), - (1, 9, 12), - (9, 1, 13), - (6, 3, 14), - (4, 8, 15), - (8, 3, 16), - (5, 10, 17), - (10, 5, 18), - (10, 8, 19), - (1, 11, 20), - (11, 1, 21), - (9, 11, 22), - (11, 9, 23), - ]; - - let graph = Graph::new(); - - for (src, dst, t) in edges { - graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + let expected: HashMap = HashMap::from([ + ("10".to_string(), 0.072082), + ("8".to_string(), 0.136473), + ("3".to_string(), 0.15484), + ("6".to_string(), 0.07208), + ("11".to_string(), 0.06186), + ("2".to_string(), 0.03557), + ("1".to_string(), 0.11284), + ("4".to_string(), 0.07944), + ("7".to_string(), 0.01638), + ("9".to_string(), 0.06186), + ("5".to_string(), 0.19658), + ]); + + let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None) + .to_hashmap(|value| value.score); + + assert_eq_hashmaps_approx(&results, &expected, 1e-5); + }); } - test_storage!(&graph, |graph| { - let expected: HashMap = HashMap::from([ - ("10".to_string(), 0.072082), - ("8".to_string(), 0.136473), - ("3".to_string(), 0.15484), - ("6".to_string(), 0.07208), - ("11".to_string(), 0.06186), - ("2".to_string(), 0.03557), - ("1".to_string(), 0.11284), - ("4".to_string(), 0.07944), - ("7".to_string(), 0.01638), - ("9".to_string(), 0.06186), - ("5".to_string(), 0.19658), - ]); - - let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None) - .to_hashmap(|value| value.score); - - assert_eq_hashmaps_approx(&results, &expected, 1e-5); - }); -} - -#[test] -fn two_nodes_page_rank() { - let edges = vec![(1, 2), (2, 1)]; + #[test] + fn two_nodes_page_rank() { + let edges = vec![(1, 2), (2, 1)]; - let graph = Graph::new(); + let graph = Graph::new(); - for (t, (src, dst)) in edges.into_iter().enumerate() { - graph.add_edge(t as i64, src, dst, NO_PROPS, None).unwrap(); - } + for (t, (src, dst)) in edges.into_iter().enumerate() { + graph.add_edge(t as i64, src, dst, NO_PROPS, None).unwrap(); + } - test_storage!(&graph, |graph| { - let expected: HashMap = - HashMap::from([("1".to_string(), 0.5), ("2".to_string(), 0.5)]); + test_storage!(&graph, |graph| { + let expected: HashMap = + HashMap::from([("1".to_string(), 0.5), ("2".to_string(), 0.5)]); - let results = unweighted_page_rank(graph, Some(1000), Some(4), None, false, None) - .to_hashmap(|value| value.score); + let results = unweighted_page_rank(graph, Some(1000), Some(4), None, false, None) + .to_hashmap(|value| value.score); - assert_eq_hashmaps_approx(&results, &expected, 1e-3); - }); -} + assert_eq_hashmaps_approx(&results, &expected, 1e-3); + }); + } -#[test] -fn three_nodes_page_rank_one_dangling() { - let edges = vec![(1, 2), (2, 1), (2, 3)]; + #[test] + fn three_nodes_page_rank_one_dangling() { + let edges = vec![(1, 2), (2, 1), (2, 3)]; - let graph = Graph::new(); + let graph = Graph::new(); - for (t, (src, dst)) in edges.into_iter().enumerate() { - graph.add_edge(t as i64, src, dst, NO_PROPS, None).unwrap(); - } + for (t, (src, dst)) in edges.into_iter().enumerate() { + graph.add_edge(t as i64, src, dst, NO_PROPS, None).unwrap(); + } - test_storage!(&graph, |graph| { - let expected: HashMap = HashMap::from([ - ("1".to_string(), 0.303), - ("2".to_string(), 0.393), - ("3".to_string(), 0.303), - ]); + test_storage!(&graph, |graph| { + let expected: HashMap = HashMap::from([ + ("1".to_string(), 0.303), + ("2".to_string(), 0.393), + ("3".to_string(), 0.303), + ]); - let results = unweighted_page_rank(graph, Some(10), Some(4), None, false, None) - .to_hashmap(|value| value.score); + let results = unweighted_page_rank(graph, Some(10), Some(4), None, false, None) + .to_hashmap(|value| value.score); - assert_eq_hashmaps_approx(&results, &expected, 1e-3); - }); -} + assert_eq_hashmaps_approx(&results, &expected, 1e-3); + }); + } -#[test] -fn dangling_page_rank() { - let edges = vec![ - (1, 2), - (1, 3), - (2, 3), - (3, 1), - (3, 2), - (3, 4), - // dangling from here - (4, 5), - (5, 6), - (6, 7), - (7, 8), - (8, 9), - (9, 10), - (10, 11), - ] - .into_iter() - .enumerate() - .map(|(t, (src, dst))| (src, dst, t as i64)) - .collect_vec(); - - let graph = Graph::new(); - - for (src, dst, t) in edges { - graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + #[test] + fn dangling_page_rank() { + let edges = vec![ + (1, 2), + (1, 3), + (2, 3), + (3, 1), + (3, 2), + (3, 4), + // dangling from here + (4, 5), + (5, 6), + (6, 7), + (7, 8), + (8, 9), + (9, 10), + (10, 11), + ] + .into_iter() + .enumerate() + .map(|(t, (src, dst))| (src, dst, t as i64)) + .collect_vec(); + + let graph = Graph::new(); + + for (src, dst, t) in edges { + graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let expected: HashMap = HashMap::from([ + ("1".to_string(), 0.055), + ("2".to_string(), 0.079), + ("3".to_string(), 0.113), + ("4".to_string(), 0.055), + ("5".to_string(), 0.070), + ("6".to_string(), 0.083), + ("7".to_string(), 0.093), + ("8".to_string(), 0.102), + ("9".to_string(), 0.110), + ("10".to_string(), 0.117), + ("11".to_string(), 0.122), + ]); + + let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None) + .to_hashmap(|value| value.score); + + assert_eq_hashmaps_approx(&results, &expected, 1e-3); + }); } - test_storage!(&graph, |graph| { - let expected: HashMap = HashMap::from([ - ("1".to_string(), 0.055), - ("2".to_string(), 0.079), - ("3".to_string(), 0.113), - ("4".to_string(), 0.055), - ("5".to_string(), 0.070), - ("6".to_string(), 0.083), - ("7".to_string(), 0.093), - ("8".to_string(), 0.102), - ("9".to_string(), 0.110), - ("10".to_string(), 0.117), - ("11".to_string(), 0.122), - ]); - - let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None) - .to_hashmap(|value| value.score); - - assert_eq_hashmaps_approx(&results, &expected, 1e-3); - }); } diff --git a/raphtory/tests/algo_tests/community_detection.rs b/raphtory/tests/algo_tests/community_detection.rs index b07c3b70b2..8167f878e0 100644 --- a/raphtory/tests/algo_tests/community_detection.rs +++ b/raphtory/tests/algo_tests/community_detection.rs @@ -1,217 +1,220 @@ -use raphtory::{ - algorithms::community_detection::{ - label_propagation::label_propagation, - louvain::louvain, - modularity::{ComID, ModularityFunction, ModularityUnDir, Partition}, - }, - logging::global_info_logger, - prelude::*, - test_storage, -}; -use raphtory_core::entities::VID; -use std::collections::{HashMap, HashSet}; -use tracing::info; - -fn group_by_value(map: &HashMap) -> Vec> { - let mut grouped: HashMap> = HashMap::new(); - - for (key, &value) in map { - grouped - .entry(value) - .or_insert_with(HashSet::new) - .insert(key.clone()); - } - - grouped.into_values().collect() -} +#[cfg(all(test, feature = "test-utils"))] +mod test { + use raphtory::{ + algorithms::community_detection::{ + label_propagation::label_propagation, + louvain::louvain, + modularity::{ComID, ModularityFunction, ModularityUnDir, Partition}, + }, + logging::global_info_logger, + prelude::*, + test_storage, + }; + use raphtory_core::entities::VID; + use std::collections::{HashMap, HashSet}; + use tracing::info; + + fn group_by_value(map: &HashMap) -> Vec> { + let mut grouped: HashMap> = HashMap::new(); + + for (key, &value) in map { + grouped + .entry(value) + .or_insert_with(HashSet::new) + .insert(key.clone()); + } -#[test] -fn lpa_test() { - let graph: Graph = Graph::new(); - let edges = vec![ - (1, "R1", "R2"), - (1, "R1", "R3"), - (1, "R2", "R3"), - (1, "R3", "G"), - (1, "G", "B1"), - (1, "G", "B3"), - (1, "B1", "B2"), - (1, "B2", "B3"), - (1, "B2", "B4"), - (1, "B3", "B4"), - (1, "B3", "B5"), - (1, "B4", "B5"), - ]; - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + grouped.into_values().collect() } - test_storage!(&graph, |graph| { - let seed = Some([5; 32]); - let result = - label_propagation(graph, 20, seed, None).to_hashmap(|value| value.community_id); - println!("{:?}", result); - let result = group_by_value(&result); - - let expected = vec![ - HashSet::from(["R1".to_string(), "R2".to_string(), "R3".to_string()]), - HashSet::from([ - "G".to_string(), - "B1".to_string(), - "B2".to_string(), - "B3".to_string(), - "B4".to_string(), - "B5".to_string(), - ]), + + #[test] + fn lpa_test() { + let graph: Graph = Graph::new(); + let edges = vec![ + (1, "R1", "R2"), + (1, "R1", "R3"), + (1, "R2", "R3"), + (1, "R3", "G"), + (1, "G", "B1"), + (1, "G", "B3"), + (1, "B1", "B2"), + (1, "B2", "B3"), + (1, "B2", "B4"), + (1, "B3", "B4"), + (1, "B3", "B5"), + (1, "B4", "B5"), ]; - for hashset in expected { - assert!(result.contains(&hashset)); + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); } - }); -} + test_storage!(&graph, |graph| { + let seed = Some([5; 32]); + let result = + label_propagation(graph, 20, seed, None).to_hashmap(|value| value.community_id); + println!("{:?}", result); + let result = group_by_value(&result); + + let expected = vec![ + HashSet::from(["R1".to_string(), "R2".to_string(), "R3".to_string()]), + HashSet::from([ + "G".to_string(), + "B1".to_string(), + "B2".to_string(), + "B3".to_string(), + "B4".to_string(), + "B5".to_string(), + ]), + ]; + for hashset in expected { + assert!(result.contains(&hashset)); + } + }); + } -use proptest::prelude::*; - -#[test] -fn test_louvain() { - let edges = vec![ - (100, 200, 2.0f64), - (100, 300, 3.0f64), - (200, 300, 8.5f64), - (300, 400, 1.0f64), - (400, 500, 1.5f64), - (600, 800, 0.5f64), - (700, 900, 3.5f64), - (100, 600, 1.5f64), - ]; - test_all_nodes_assigned_inner(edges) -} + use proptest::prelude::*; -fn test_all_nodes_assigned_inner(edges: Vec<(u64, u64, f64)>) { - let graph = Graph::new(); - for (src, dst, weight) in edges { - graph - .add_edge(1, src, dst, [("weight", weight)], None) - .unwrap(); - graph - .add_edge(1, dst, src, [("weight", weight)], None) - .unwrap(); + #[test] + fn test_louvain() { + let edges = vec![ + (100, 200, 2.0f64), + (100, 300, 3.0f64), + (200, 300, 8.5f64), + (300, 400, 1.0f64), + (400, 500, 1.5f64), + (600, 800, 0.5f64), + (700, 900, 3.5f64), + (100, 600, 1.5f64), + ]; + test_all_nodes_assigned_inner(edges) } - test_storage!(&graph, |graph| { - let result = louvain::(graph, 1.0, Some("weight"), None); - assert!(graph - .nodes() - .iter() - .all(|n| result.get_by_node(n).is_some())); - }); -} + fn test_all_nodes_assigned_inner(edges: Vec<(u64, u64, f64)>) { + let graph = Graph::new(); + for (src, dst, weight) in edges { + graph + .add_edge(1, src, dst, [("weight", weight)], None) + .unwrap(); + graph + .add_edge(1, dst, src, [("weight", weight)], None) + .unwrap(); + } -fn test_all_nodes_assigned_inner_unweighted(edges: Vec<(u64, u64)>) { - let graph = Graph::new(); - for (src, dst) in edges { - graph.add_edge(1, src, dst, NO_PROPS, None).unwrap(); - graph.add_edge(1, dst, src, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + let result = louvain::(graph, 1.0, Some("weight"), None); + assert!(graph + .nodes() + .iter() + .all(|n| result.get_by_node(n).is_some())); + }); } - test_storage!(&graph, |graph| { - let result = louvain::(graph, 1.0, None, None); - assert!(graph - .nodes() - .iter() - .all(|n| result.get_by_node(n).is_some())); - }); -} + fn test_all_nodes_assigned_inner_unweighted(edges: Vec<(u64, u64)>) { + let graph = Graph::new(); + for (src, dst) in edges { + graph.add_edge(1, src, dst, NO_PROPS, None).unwrap(); + graph.add_edge(1, dst, src, NO_PROPS, None).unwrap(); + } -proptest! { - #[test] - fn test_all_nodes_in_communities(edges in any::>().prop_map(|mut v| {v.iter_mut().for_each(|(_, _, w)| *w = w.abs()); v})) { - test_all_nodes_assigned_inner(edges) + test_storage!(&graph, |graph| { + let result = louvain::(graph, 1.0, None, None); + assert!(graph + .nodes() + .iter() + .all(|n| result.get_by_node(n).is_some())); + }); } - #[test] - fn test_all_nodes_assigned_unweighted(edges in any::>().prop_map(|v| v.into_iter().map(|(s, d)| (s as u64, d as u64)).collect::>())) { - test_all_nodes_assigned_inner_unweighted(edges) - } -} + proptest! { + #[test] + fn test_all_nodes_in_communities(edges in any::>().prop_map(|mut v| {v.iter_mut().for_each(|(_, _, w)| *w = w.abs()); v})) { + test_all_nodes_assigned_inner(edges) + } -#[cfg(feature = "io")] -#[test] -fn lfr_test() { - use raphtory::io::csv_loader::CsvLoader; - use raphtory_api::core::utils::logging::global_info_logger; - use serde::{Deserialize, Serialize}; - use std::path::PathBuf; - global_info_logger(); - let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - d.push("resources/test"); - let loader = CsvLoader::new(d.join("test.csv")).set_delimiter(","); - let graph = Graph::new(); - - #[derive(Deserialize, Serialize, Debug)] - struct CsvEdge { - src: u64, - dst: u64, + #[test] + fn test_all_nodes_assigned_unweighted(edges in any::>().prop_map(|v| v.into_iter().map(|(s, d)| (s as u64, d as u64)).collect::>())) { + test_all_nodes_assigned_inner_unweighted(edges) + } } - loader - .load_into_graph(&graph, |e: CsvEdge, g| { - g.add_edge(1, e.src, e.dst, NO_PROPS, None).unwrap(); - }) - .unwrap(); + #[cfg(feature = "io")] + #[test] + fn lfr_test() { + use raphtory::io::csv_loader::CsvLoader; + use raphtory_api::core::utils::logging::global_info_logger; + use serde::{Deserialize, Serialize}; + use std::path::PathBuf; + global_info_logger(); + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.push("resources/test"); + let loader = CsvLoader::new(d.join("test.csv")).set_delimiter(","); + let graph = Graph::new(); + + #[derive(Deserialize, Serialize, Debug)] + struct CsvEdge { + src: u64, + dst: u64, + } + + loader + .load_into_graph(&graph, |e: CsvEdge, g| { + g.add_edge(1, e.src, e.dst, NO_PROPS, None).unwrap(); + }) + .unwrap(); - test_storage!(&graph, |graph| { - let _ = louvain::(graph, 1.0, None, None); - // TODO: Add assertions - }); -} + test_storage!(&graph, |graph| { + let _ = louvain::(graph, 1.0, None, None); + // TODO: Add assertions + }); + } -#[test] -fn test_delta() { - global_info_logger(); - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let mut m = ModularityUnDir::new( - graph, - None, - 1.0, - Partition::new_singletons(graph.count_nodes()), - 1e-8, - ); - let old_value = m.value(); - assert_eq!(old_value, -0.5); - let delta = m.move_delta(&VID(0), ComID(1)); - info!("delta: {delta}"); - m.move_node(&VID(0), ComID(1)); - assert_eq!(m.value(), old_value + delta) - }); -} + #[test] + fn test_delta() { + global_info_logger(); + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let mut m = ModularityUnDir::new( + graph, + None, + 1.0, + Partition::new_singletons(graph.count_nodes()), + 1e-8, + ); + let old_value = m.value(); + assert_eq!(old_value, -0.5); + let delta = m.move_delta(&VID(0), ComID(1)); + info!("delta: {delta}"); + m.move_node(&VID(0), ComID(1)); + assert_eq!(m.value(), old_value + delta) + }); + } -#[test] -fn test_aggregation() { - global_info_logger(); - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 0, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); - graph.add_edge(0, 0, 3, NO_PROPS, None).unwrap(); - graph.add_edge(0, 3, 0, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let partition = Partition::from_iter([0usize, 0, 1, 1]); - let mut m = ModularityUnDir::new(graph, None, 1.0, partition, 1e-8); - let value_before = m.value(); - let _ = m.aggregate(); - let value_after = m.value(); - info!("before: {value_before}, after: {value_after}"); - assert_eq!(value_after, value_before); - let delta = m.move_delta(&VID(0), ComID(1)); - m.move_node(&VID(0), ComID(1)); - let value_merged = m.value(); - assert_eq!(value_merged, 0.0); - assert!((value_merged - (value_after + delta)).abs() < 1e-8); - }); + #[test] + fn test_aggregation() { + global_info_logger(); + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 0, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); + graph.add_edge(0, 0, 3, NO_PROPS, None).unwrap(); + graph.add_edge(0, 3, 0, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let partition = Partition::from_iter([0usize, 0, 1, 1]); + let mut m = ModularityUnDir::new(graph, None, 1.0, partition, 1e-8); + let value_before = m.value(); + let _ = m.aggregate(); + let value_after = m.value(); + info!("before: {value_before}, after: {value_after}"); + assert_eq!(value_after, value_before); + let delta = m.move_delta(&VID(0), ComID(1)); + m.move_node(&VID(0), ComID(1)); + let value_merged = m.value(); + assert_eq!(value_merged, 0.0); + assert!((value_merged - (value_after + delta)).abs() < 1e-8); + }); + } } diff --git a/raphtory/tests/algo_tests/components.rs b/raphtory/tests/algo_tests/components.rs index d11e560e32..ca449e5a18 100644 --- a/raphtory/tests/algo_tests/components.rs +++ b/raphtory/tests/algo_tests/components.rs @@ -1,949 +1,954 @@ -use ahash::HashSet; -use proptest::{prelude::Strategy, proptest, sample::Index}; -use raphtory::{ - algorithms::components::{weakly_connected_components, ConnectedComponent}, - db::api::{ - mutation::AdditionOps, - state::{NodeStateValue, TypedNodeState}, - view::internal::GraphView, - }, - prelude::*, - test_storage, -}; -use serde::{Deserialize, Serialize}; -use std::{ - collections::{BTreeSet, HashMap}, - fmt::{Debug, Formatter}, - hash::Hash, -}; - -fn assert_same_partition< - V: NodeStateValue + Hash + Eq + 'static, - G: GraphView + 'static, - ID: Into, ->( - left: TypedNodeState<'static, V, G>, // NodeState, - right: impl IntoIterator>, -) { - let left_groups: HashSet> = left - .groups() - .into_iter_groups() - .map(|(_, nodes)| nodes.id().collect()) - .collect(); - let right_groups: HashSet> = right - .into_iter() - .map(|inner| inner.into_iter().map(|id| id.into()).collect()) - .collect(); - assert_eq!(left_groups, right_groups); -} - -#[test] -fn run_loop_simple_connected_components() { - let graph = Graph::new(); - - let edges = vec![ - (1, 2, 1), - (2, 3, 2), - (3, 4, 3), - (3, 5, 4), - (6, 5, 5), - (7, 8, 6), - (8, 7, 7), - ]; - - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - for _ in 0..1000 { - let results = weakly_connected_components(graph); - assert_same_partition(results, [1..=6, 7..=8]); - } - }); -} - -#[test] -fn simple_connected_components_2() { - let graph = Graph::new(); - - let edges = vec![ - (1, 2, 1), - (1, 3, 2), - (1, 4, 3), - (3, 1, 4), - (3, 4, 5), - (3, 5, 6), - (4, 5, 7), - (5, 6, 8), - (5, 8, 9), - (7, 5, 10), - (8, 5, 11), - (1, 9, 12), - (9, 1, 13), - (6, 3, 14), - (4, 8, 15), - (8, 3, 16), - (5, 10, 17), - (10, 5, 18), - (10, 8, 19), - (1, 11, 20), - (11, 1, 21), - (9, 11, 22), - (11, 9, 23), - ]; - - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let results = weakly_connected_components(graph); - assert_same_partition(results, [1..=11]); - }); -} - -#[test] -fn test_multiple_components() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (2, 2, 1), - (3, 3, 1), - (1, 10, 11), - (2, 20, 21), - (3, 30, 31), - ]; - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - for _ in 0..1000 { - let result = weakly_connected_components(&graph); - assert_same_partition( - result, - [vec![1, 2, 3], vec![10, 11], vec![20, 21], vec![30, 31]], - ) - } -} - -// connected community_detection on a graph with 1 node and a self loop -#[test] -fn simple_connected_components_3() { - let graph = Graph::new(); - - let edges = vec![(1, 1, 1)]; - - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - for _ in 0..1000 { - // loop to test for weird non-deterministic behaviour - let results = weakly_connected_components(graph); - assert_same_partition(results, [[1]]); - } - }); -} - -#[test] -fn windowed_connected_components() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).expect("add edge"); - graph.add_edge(0, 2, 1, NO_PROPS, None).expect("add edge"); - graph.add_edge(9, 3, 4, NO_PROPS, None).expect("add edge"); - graph.add_edge(9, 4, 3, NO_PROPS, None).expect("add edge"); - - test_storage!(&graph, |graph| { - let results = weakly_connected_components(graph); - assert_same_partition(results, [[1, 2], [3, 4]]); - - let wg = graph.window(0, 2); - let results = weakly_connected_components(&wg); - assert_same_partition(results, [[1, 2]]); - }); -} - -#[test] -fn layered_connected_components() { - let g = Graph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("ZERO-TWO")).unwrap(); - g.add_edge(1, 1, 3, NO_PROPS, Some("ZERO-TWO")).unwrap(); - g.add_edge(2, 4, 5, NO_PROPS, Some("ZERO-TWO")).unwrap(); - g.add_edge(3, 6, 7, NO_PROPS, Some("THREE-FIVE")).unwrap(); - g.add_edge(4, 8, 9, NO_PROPS, Some("THREE-FIVE")).unwrap(); - - let g_layer_zero_two = g.layers("ZERO-TWO").unwrap(); - - assert_eq!(g_layer_zero_two.nodes().id(), [1, 2, 3, 4, 5]); - let g_layer_three_five = g.layers("THREE-FIVE").unwrap(); - - let res_zero_two = weakly_connected_components(&g_layer_zero_two); - let c1 = res_zero_two.get_by_node(1).unwrap(); - let c2 = res_zero_two.get_by_node(4).unwrap(); - - let expected_zero_two: HashMap = - [(1, c1), (2, c1), (3, c1), (4, c2), (5, c2)].into(); - - assert_eq!(res_zero_two, expected_zero_two); - - let res_three_five = weakly_connected_components(&g_layer_three_five); - - let c6 = res_three_five.get_by_node(6).unwrap().component_id; - let c7 = res_three_five.get_by_node(8).unwrap().component_id; - - let expected_three_five: HashMap = [(6u64, c6), (7, c6), (8, c7), (9, c7)].into(); - assert_eq!(res_three_five, expected_three_five); -} - -#[derive(Serialize, Deserialize)] -struct ComponentTestInput { - edges: Vec<(u64, u64)>, - components: Vec>, -} - -impl Debug for ComponentTestInput { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&serde_json::to_string(self).unwrap()) - } -} - -fn random_component_edges( - num_components: usize, - num_nodes_per_component: usize, -) -> impl Strategy { - let vs = proptest::collection::vec( - proptest::collection::vec( - ( - 0..num_nodes_per_component, - proptest::arbitrary::any::(), - ), - 2..=num_nodes_per_component, - ), - 0..=num_components, - ); - vs.prop_map(move |vs| { - let mut edges = Vec::new(); - let mut components = Vec::new(); - for (ci, c) in vs.into_iter().enumerate() { - let offset = num_nodes_per_component * ci; - let component: Vec<_> = c.iter().map(|(i, _)| (*i + offset) as u64).collect(); - for i in 1..c.len() { - let n = component[c[i].1.index(i)]; - edges.push((component[i], n)); - } - components.push(component.into_iter().collect()); - } - ComponentTestInput { edges, components } - }) -} - -#[test] -fn weakly_connected_components_proptest() { - proptest!(|(input in random_component_edges(10, 100))|{ - let ComponentTestInput {edges, components } = input; - let g = Graph::new(); - for (src, dst) in edges { - g.add_edge(0, src, dst, NO_PROPS, None).unwrap(); - } - for _ in 0..10 { - let result = weakly_connected_components(&g); - assert_same_partition(result, &components); - } - }) -} - -mod in_component_test { - use itertools::Itertools; +#[cfg(all(test, feature = "test-utils"))] +mod test { + use ahash::HashSet; + use proptest::{prelude::Strategy, proptest, sample::Index}; use raphtory::{ - algorithms::components::{ - in_component, in_component_filtered, in_components, in_components_filtered, - }, - db::{ - api::mutation::AdditionOps, - graph::views::filter::{ - model::{ - graph_filter::GraphFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, TryAsCompositeFilter, ViewWrapOps, - }, - CreateFilter, - }, + algorithms::components::{weakly_connected_components, ConnectedComponent}, + db::api::{ + mutation::AdditionOps, + state::{NodeStateValue, TypedNodeState}, + view::internal::GraphView, }, prelude::*, test_storage, }; - use std::collections::HashMap; - - fn check_node(graph: &Graph, node_id: u64, mut correct: Vec<(u64, usize)>) { - let mut results: Vec<_> = in_component(graph.node(node_id).unwrap()) - .iter() - .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) - .collect(); - results.sort(); - correct.sort(); - assert_eq!(results, correct); - } + use serde::{Deserialize, Serialize}; + use std::{ + collections::{BTreeSet, HashMap}, + fmt::{Debug, Formatter}, + hash::Hash, + }; - fn check_node_filtered( - graph: &Graph, - node_id: u64, - filter: F, - mut correct: Vec<(u64, usize)>, + fn assert_same_partition< + V: NodeStateValue + Hash + Eq + 'static, + G: GraphView + 'static, + ID: Into, + >( + left: TypedNodeState<'static, V, G>, // NodeState, + right: impl IntoIterator>, ) { - let mut results: Vec<_> = in_component_filtered(graph.node(node_id).unwrap(), filter) - .unwrap() - .iter() - .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) + let left_groups: HashSet> = left + .groups() + .into_iter_groups() + .map(|(_, nodes)| nodes.id().collect()) .collect(); - - results.sort(); - correct.sort(); - assert_eq!(results, correct); + let right_groups: HashSet> = right + .into_iter() + .map(|inner| inner.into_iter().map(|id| id.into()).collect()) + .collect(); + assert_eq!(left_groups, right_groups); } #[test] - fn in_component_test() { + fn run_loop_simple_connected_components() { let graph = Graph::new(); + let edges = vec![ - (1, 1, 2), - (1, 1, 3), - (1, 2, 4), - (1, 2, 5), - (1, 5, 4), - (1, 4, 6), - (1, 4, 7), - (1, 5, 8), + (1, 2, 1), + (2, 3, 2), + (3, 4, 3), + (3, 5, 4), + (6, 5, 5), + (7, 8, 6), + (8, 7, 7), ]; - for (ts, src, dst) in edges { + for (src, dst, ts) in edges { graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); } - check_node(&graph, 1, vec![]); - check_node(&graph, 2, vec![(1, 1)]); - check_node(&graph, 3, vec![(1, 1)]); - check_node(&graph, 4, vec![(1, 2), (2, 1), (5, 1)]); - check_node(&graph, 5, vec![(1, 2), (2, 1)]); - check_node(&graph, 6, vec![(1, 3), (2, 2), (4, 1), (5, 2)]); - check_node(&graph, 7, vec![(1, 3), (2, 2), (4, 1), (5, 2)]); - check_node(&graph, 8, vec![(1, 3), (2, 2), (5, 1)]); + test_storage!(&graph, |graph| { + for _ in 0..1000 { + let results = weakly_connected_components(graph); + assert_same_partition(results, [1..=6, 7..=8]); + } + }); } #[test] - fn test_distances() { + fn simple_connected_components_2() { let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); - graph.add_edge(0, 5, 3, NO_PROPS, None).unwrap(); - - check_node(&graph, 3, vec![(1, 2), (2, 1), (4, 2), (5, 1)]); - } - #[test] - fn in_components_test() { - let graph = Graph::new(); let edges = vec![ - (1, 1, 2), - (1, 1, 3), - (1, 2, 4), - (1, 2, 5), - (1, 5, 4), - (1, 4, 6), - (1, 4, 7), - (1, 5, 8), + (1, 2, 1), + (1, 3, 2), + (1, 4, 3), + (3, 1, 4), + (3, 4, 5), + (3, 5, 6), + (4, 5, 7), + (5, 6, 8), + (5, 8, 9), + (7, 5, 10), + (8, 5, 11), + (1, 9, 12), + (9, 1, 13), + (6, 3, 14), + (4, 8, 15), + (8, 3, 16), + (5, 10, 17), + (10, 5, 18), + (10, 8, 19), + (1, 11, 20), + (11, 1, 21), + (9, 11, 22), + (11, 9, 23), ]; - for (ts, src, dst) in edges { + for (src, dst, ts) in edges { graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); } test_storage!(&graph, |graph| { - let results = in_components(graph, None); - let mut correct = HashMap::new(); - correct.insert("1".to_string(), vec![]); - correct.insert("2".to_string(), vec![1]); - correct.insert("3".to_string(), vec![1]); - correct.insert("4".to_string(), vec![1, 2, 5]); - correct.insert("5".to_string(), vec![1, 2]); - correct.insert("6".to_string(), vec![1, 2, 4, 5]); - correct.insert("7".to_string(), vec![1, 2, 4, 5]); - correct.insert("8".to_string(), vec![1, 2, 5]); - let map: HashMap> = results - .into_iter() - .map(|(k, v)| { - ( - k.name(), - v.in_components - .into_iter() - .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) - .sorted() - .collect(), - ) - }) - .collect(); - assert_eq!(map, correct); + let results = weakly_connected_components(graph); + assert_same_partition(results, [1..=11]); }); } #[test] - fn in_component_filtered_by_layer_removes_cross_layer_ancestors() { + fn test_multiple_components() { let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); - - graph.add_edge(1, 2, 5, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 5, 4, NO_PROPS, Some("B")).unwrap(); - - // Sanity: with A-only filter, 5 should disappear - let filter = GraphFilter.layer("A"); - - check_node_filtered(&graph, 6, filter.clone(), vec![(4, 1), (2, 2), (1, 3)]); - check_node_filtered(&graph, 4, filter.clone(), vec![(2, 1), (1, 2)]); - check_node_filtered(&graph, 5, filter.clone(), vec![]); // B-only inbound edges, so none under A + let edges = vec![ + (1, 1, 2), + (2, 2, 1), + (3, 3, 1), + (1, 10, 11), + (2, 20, 21), + (3, 30, 31), + ]; + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + for _ in 0..1000 { + let result = weakly_connected_components(&graph); + assert_same_partition( + result, + [vec![1, 2, 3], vec![10, 11], vec![20, 21], vec![30, 31]], + ) + } } + // connected community_detection on a graph with 1 node and a self loop #[test] - fn in_components_filtered_by_layer_matches_expected_node_sets() { + fn simple_connected_components_3() { let graph = Graph::new(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); + let edges = vec![(1, 1, 1)]; - graph.add_edge(1, 2, 5, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 5, 4, NO_PROPS, Some("B")).unwrap(); + for (src, dst, ts) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } test_storage!(&graph, |graph| { - let results = in_components_filtered(graph, None, GraphFilter.layer("A")).unwrap(); - - let mut correct: HashMap> = HashMap::new(); - correct.insert("1".to_string(), vec![]); - correct.insert("2".to_string(), vec![1]); - correct.insert("4".to_string(), vec![1, 2]); - correct.insert("5".to_string(), vec![]); // only reachable via B edges, which are filtered out - correct.insert("6".to_string(), vec![1, 2, 4]); - - let map: HashMap> = results - .into_iter() - .map(|(k, v)| { - ( - k.name(), - v.in_components - .into_iter() - .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) - .sorted() - .collect(), - ) - }) - .collect(); - - assert_eq!(map, correct); + for _ in 0..1000 { + // loop to test for weird non-deterministic behaviour + let results = weakly_connected_components(graph); + assert_same_partition(results, [[1]]); + } }); } #[test] - fn in_component_filtered_by_layer_handles_multiple_inbound_paths_with_distances() { + fn windowed_connected_components() { let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).expect("add edge"); + graph.add_edge(0, 2, 1, NO_PROPS, None).expect("add edge"); + graph.add_edge(9, 3, 4, NO_PROPS, None).expect("add edge"); + graph.add_edge(9, 4, 3, NO_PROPS, None).expect("add edge"); - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); - - // Layer B adds an alternate chain to 4 that should be ignored under A filter - graph.add_edge(1, 10, 11, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 11, 4, NO_PROPS, Some("B")).unwrap(); + test_storage!(&graph, |graph| { + let results = weakly_connected_components(graph); + assert_same_partition(results, [[1, 2], [3, 4]]); - check_node_filtered( - &graph, - 6, - GraphFilter.layer("A"), - vec![(4, 1), (2, 2), (3, 2), (1, 3)], - ); + let wg = graph.window(0, 2); + let results = weakly_connected_components(&wg); + assert_same_partition(results, [[1, 2]]); + }); } #[test] - fn in_component_filtered_returns_nodes_that_are_unfiltered_for_future_traversals() { - let graph = Graph::new(); + fn layered_connected_components() { + let g = Graph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("ZERO-TWO")).unwrap(); + g.add_edge(1, 1, 3, NO_PROPS, Some("ZERO-TWO")).unwrap(); + g.add_edge(2, 4, 5, NO_PROPS, Some("ZERO-TWO")).unwrap(); + g.add_edge(3, 6, 7, NO_PROPS, Some("THREE-FIVE")).unwrap(); + g.add_edge(4, 8, 9, NO_PROPS, Some("THREE-FIVE")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 99, 2, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 100, 99, NO_PROPS, Some("B")).unwrap(); - - let result = in_component_filtered(graph.node(4).unwrap(), GraphFilter.layer("A")).unwrap(); - let result = result - .iter() - .flat_map(|(n, _)| n.id().as_u64()) - .collect::>(); - assert_eq!(result, vec![2]); - let unfiltered_ids: Vec = - in_component_filtered(graph.node(4).unwrap(), GraphFilter.layer("A")) - .unwrap() - .nodes() - .in_neighbours() - .iter() - .flat_map(|(_, ns)| ns.iter().filter_map(|c| c.id().as_u64())) - .collect(); + let g_layer_zero_two = g.layers("ZERO-TWO").unwrap(); - assert_eq!(unfiltered_ids, vec![99]); - } + assert_eq!(g_layer_zero_two.nodes().id(), [1, 2, 3, 4, 5]); + let g_layer_three_five = g.layers("THREE-FIVE").unwrap(); - #[test] - fn in_component_edge_filtered_handles_multiple_inbound_paths_with_distances() { - let graph = Graph::new(); + let res_zero_two = weakly_connected_components(&g_layer_zero_two); + let c1 = res_zero_two.get_by_node(1).unwrap(); + let c2 = res_zero_two.get_by_node(4).unwrap(); - graph - .add_edge(1, 1, 2, vec![("p1", Prop::U64(1))], Some("A")) - .unwrap(); - graph - .add_edge(1, 2, 4, vec![("p1", Prop::U64(2))], Some("A")) - .unwrap(); - graph - .add_edge(1, 4, 6, vec![("p1", Prop::U64(3))], Some("A")) - .unwrap(); - graph - .add_edge(1, 3, 4, vec![("p1", Prop::U64(4))], Some("A")) - .unwrap(); - - // Layer B adds an alternate chain to 4 that should be ignored under A filter - graph - .add_edge(1, 10, 11, vec![("p1", Prop::U64(2))], Some("B")) - .unwrap(); - graph - .add_edge(1, 11, 4, vec![("p1", Prop::U64(2))], Some("B")) - .unwrap(); - - let filter = EdgeFilter.layer("A").property("p1").ge(3u64); - check_node_filtered(&graph, 6, filter, vec![(3, 2), (4, 1)]); - } -} + let expected_zero_two: HashMap = + [(1, c1), (2, c1), (3, c1), (4, c2), (5, c2)].into(); -#[cfg(test)] -mod components_test { - use itertools::Itertools; - use raphtory::{ - algorithms::components::{ - out_component, out_component_filtered, out_components, out_components_filtered, - }, - db::{ - api::mutation::AdditionOps, - graph::views::filter::{ - model::{ - graph_filter::GraphFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, ViewWrapOps, - }, - CreateFilter, - }, - }, - prelude::*, - test_storage, - }; - use std::collections::HashMap; + assert_eq!(res_zero_two, expected_zero_two); - fn check_node(graph: &Graph, node_id: u64, mut correct: Vec<(u64, usize)>) { - let mut results: Vec<_> = out_component(graph.node(node_id).unwrap()) - .iter() - .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) - .collect(); - results.sort(); - correct.sort(); - assert_eq!(results, correct); - } + let res_three_five = weakly_connected_components(&g_layer_three_five); - fn check_node_filtered( - graph: &Graph, - node_id: u64, - filter: F, - mut correct: Vec<(u64, usize)>, - ) { - let mut results: Vec<_> = out_component_filtered(graph.node(node_id).unwrap(), filter) - .unwrap() - .iter() - .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) - .collect(); - results.sort(); - correct.sort(); - assert_eq!(results, correct); + let c6 = res_three_five.get_by_node(6).unwrap().component_id; + let c7 = res_three_five.get_by_node(8).unwrap().component_id; + + let expected_three_five: HashMap = + [(6u64, c6), (7, c6), (8, c7), (9, c7)].into(); + assert_eq!(res_three_five, expected_three_five); } - #[test] - fn out_component_test() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (1, 1, 3), - (1, 2, 3), - (1, 2, 4), - (1, 2, 5), - (1, 5, 4), - (1, 4, 6), - (1, 4, 7), - (1, 5, 8), - ]; + #[derive(Serialize, Deserialize)] + struct ComponentTestInput { + edges: Vec<(u64, u64)>, + components: Vec>, + } - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + impl Debug for ComponentTestInput { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(&serde_json::to_string(self).unwrap()) } + } - check_node( - &graph, - 1, - vec![(2, 1), (3, 1), (4, 2), (5, 2), (6, 3), (7, 3), (8, 3)], - ); - check_node( - &graph, - 2, - vec![(3, 1), (4, 1), (5, 1), (6, 2), (7, 2), (8, 2)], + fn random_component_edges( + num_components: usize, + num_nodes_per_component: usize, + ) -> impl Strategy { + let vs = proptest::collection::vec( + proptest::collection::vec( + ( + 0..num_nodes_per_component, + proptest::arbitrary::any::(), + ), + 2..=num_nodes_per_component, + ), + 0..=num_components, ); - check_node(&graph, 3, vec![]); - check_node(&graph, 4, vec![(6, 1), (7, 1)]); - check_node(&graph, 5, vec![(4, 1), (6, 2), (7, 2), (8, 1)]); - check_node(&graph, 6, vec![]); - check_node(&graph, 7, vec![]); - check_node(&graph, 8, vec![]); + vs.prop_map(move |vs| { + let mut edges = Vec::new(); + let mut components = Vec::new(); + for (ci, c) in vs.into_iter().enumerate() { + let offset = num_nodes_per_component * ci; + let component: Vec<_> = c.iter().map(|(i, _)| (*i + offset) as u64).collect(); + for i in 1..c.len() { + let n = component[c[i].1.index(i)]; + edges.push((component[i], n)); + } + components.push(component.into_iter().collect()); + } + ComponentTestInput { edges, components } + }) } #[test] - fn test_distances() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); - graph.add_edge(0, 5, 3, NO_PROPS, None).unwrap(); - - check_node(&graph, 1, vec![(2, 1), (3, 2), (4, 1), (5, 2)]); + fn weakly_connected_components_proptest() { + proptest!(|(input in random_component_edges(10, 100))|{ + let ComponentTestInput {edges, components } = input; + let g = Graph::new(); + for (src, dst) in edges { + g.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + for _ in 0..10 { + let result = weakly_connected_components(&g); + assert_same_partition(result, &components); + } + }) } - #[test] - fn out_components_test() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (1, 1, 3), - (1, 2, 4), - (1, 2, 5), - (1, 5, 4), - (1, 4, 6), - (1, 4, 7), - (1, 5, 8), - ]; + mod in_component_test { + use itertools::Itertools; + use raphtory::{ + algorithms::components::{ + in_component, in_component_filtered, in_components, in_components_filtered, + }, + db::{ + api::mutation::AdditionOps, + graph::views::filter::{ + model::{ + graph_filter::GraphFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, TryAsCompositeFilter, ViewWrapOps, + }, + CreateFilter, + }, + }, + prelude::*, + test_storage, + }; + use std::collections::HashMap; - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + fn check_node(graph: &Graph, node_id: u64, mut correct: Vec<(u64, usize)>) { + let mut results: Vec<_> = in_component(graph.node(node_id).unwrap()) + .iter() + .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) + .collect(); + results.sort(); + correct.sort(); + assert_eq!(results, correct); } - test_storage!(&graph, |graph| { - let results = out_components(graph, None); - - let mut correct = HashMap::new(); - correct.insert("1".to_string(), vec![2, 3, 4, 5, 6, 7, 8]); - correct.insert("2".to_string(), vec![4, 5, 6, 7, 8]); - correct.insert("3".to_string(), vec![]); - correct.insert("4".to_string(), vec![6, 7]); - correct.insert("5".to_string(), vec![4, 6, 7, 8]); - correct.insert("6".to_string(), vec![]); - correct.insert("7".to_string(), vec![]); - correct.insert("8".to_string(), vec![]); - let map: HashMap> = results + fn check_node_filtered( + graph: &Graph, + node_id: u64, + filter: F, + mut correct: Vec<(u64, usize)>, + ) { + let mut results: Vec<_> = in_component_filtered(graph.node(node_id).unwrap(), filter) + .unwrap() .iter() - .map(|(k, v)| { - ( - k.name(), - v.out_components - .into_iter() - .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) - .sorted() - .collect(), - ) - }) + .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) .collect(); - assert_eq!(map, correct); - }); - } - #[test] - fn out_component_filtered_by_layer_prunes_cross_layer_paths() { - let graph = Graph::new(); + results.sort(); + correct.sort(); + assert_eq!(results, correct); + } - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 3, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + #[test] + fn in_component_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 1, 3), + (1, 2, 4), + (1, 2, 5), + (1, 5, 4), + (1, 4, 6), + (1, 4, 7), + (1, 5, 8), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } - graph.add_edge(1, 2, 10, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 10, 11, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 11, 4, NO_PROPS, Some("B")).unwrap(); + check_node(&graph, 1, vec![]); + check_node(&graph, 2, vec![(1, 1)]); + check_node(&graph, 3, vec![(1, 1)]); + check_node(&graph, 4, vec![(1, 2), (2, 1), (5, 1)]); + check_node(&graph, 5, vec![(1, 2), (2, 1)]); + check_node(&graph, 6, vec![(1, 3), (2, 2), (4, 1), (5, 2)]); + check_node(&graph, 7, vec![(1, 3), (2, 2), (4, 1), (5, 2)]); + check_node(&graph, 8, vec![(1, 3), (2, 2), (5, 1)]); + } - let filter = GraphFilter.layer("A"); + #[test] + fn test_distances() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); + graph.add_edge(0, 5, 3, NO_PROPS, None).unwrap(); - check_node_filtered(&graph, 1, filter.clone(), vec![(2, 1), (3, 2), (4, 3)]); + check_node(&graph, 3, vec![(1, 2), (2, 1), (4, 2), (5, 1)]); + } - check_node_filtered(&graph, 2, filter.clone(), vec![(3, 1), (4, 2)]); + #[test] + fn in_components_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 1, 3), + (1, 2, 4), + (1, 2, 5), + (1, 5, 4), + (1, 4, 6), + (1, 4, 7), + (1, 5, 8), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } - check_node_filtered(&graph, 10, filter.clone(), vec![]); - check_node_filtered(&graph, 11, filter.clone(), vec![]); - } + test_storage!(&graph, |graph| { + let results = in_components(graph, None); + let mut correct = HashMap::new(); + correct.insert("1".to_string(), vec![]); + correct.insert("2".to_string(), vec![1]); + correct.insert("3".to_string(), vec![1]); + correct.insert("4".to_string(), vec![1, 2, 5]); + correct.insert("5".to_string(), vec![1, 2]); + correct.insert("6".to_string(), vec![1, 2, 4, 5]); + correct.insert("7".to_string(), vec![1, 2, 4, 5]); + correct.insert("8".to_string(), vec![1, 2, 5]); + let map: HashMap> = results + .into_iter() + .map(|(k, v)| { + ( + k.name(), + v.in_components + .into_iter() + .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) + .sorted() + .collect(), + ) + }) + .collect(); + assert_eq!(map, correct); + }); + } - #[test] - fn out_component_filtered_by_layer_distances_follow_filtered_graph_only() { - let graph = Graph::new(); + #[test] + fn in_component_filtered_by_layer_removes_cross_layer_ancestors() { + let graph = Graph::new(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 1, 3, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 1, 99, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 2, 5, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 5, 4, NO_PROPS, Some("B")).unwrap(); - let filter = GraphFilter.layer("A"); + // Sanity: with A-only filter, 5 should disappear + let filter = GraphFilter.layer("A"); - check_node_filtered(&graph, 1, filter, vec![(2, 1), (3, 1), (4, 2)]); - } + check_node_filtered(&graph, 6, filter.clone(), vec![(4, 1), (2, 2), (1, 3)]); + check_node_filtered(&graph, 4, filter.clone(), vec![(2, 1), (1, 2)]); + check_node_filtered(&graph, 5, filter.clone(), vec![]); // B-only inbound edges, so none under A + } - #[test] - fn out_components_filtered_by_layer_matches_expected_node_sets() { - let graph = Graph::new(); + #[test] + fn in_components_filtered_by_layer_matches_expected_node_sets() { + let graph = Graph::new(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 3, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 5, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 5, 100, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 1, 200, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 2, 5, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 5, 4, NO_PROPS, Some("B")).unwrap(); - test_storage!(&graph, |graph| { - let results = out_components_filtered(graph, None, GraphFilter.layer("A")).unwrap(); - - let mut correct: HashMap> = HashMap::new(); - correct.insert("1".to_string(), vec![2, 3, 4, 5]); - correct.insert("2".to_string(), vec![3, 4, 5]); - correct.insert("3".to_string(), vec![4]); - correct.insert("4".to_string(), vec![]); - correct.insert("5".to_string(), vec![]); - correct.insert("100".to_string(), vec![]); - correct.insert("200".to_string(), vec![]); - - let map: HashMap> = results - .into_iter() - .map(|(k, v)| { - ( - k.name(), - v.out_components - .into_iter() - .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) - .sorted() - .collect(), - ) - }) - .collect(); + test_storage!(&graph, |graph| { + let results = in_components_filtered(graph, None, GraphFilter.layer("A")).unwrap(); - assert_eq!(map, correct); - }); + let mut correct: HashMap> = HashMap::new(); + correct.insert("1".to_string(), vec![]); + correct.insert("2".to_string(), vec![1]); + correct.insert("4".to_string(), vec![1, 2]); + correct.insert("5".to_string(), vec![]); // only reachable via B edges, which are filtered out + correct.insert("6".to_string(), vec![1, 2, 4]); + + let map: HashMap> = results + .into_iter() + .map(|(k, v)| { + ( + k.name(), + v.in_components + .into_iter() + .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) + .sorted() + .collect(), + ) + }) + .collect(); + + assert_eq!(map, correct); + }); + } + + #[test] + fn in_component_filtered_by_layer_handles_multiple_inbound_paths_with_distances() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + + // Layer B adds an alternate chain to 4 that should be ignored under A filter + graph.add_edge(1, 10, 11, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 11, 4, NO_PROPS, Some("B")).unwrap(); + + check_node_filtered( + &graph, + 6, + GraphFilter.layer("A"), + vec![(4, 1), (2, 2), (3, 2), (1, 3)], + ); + } + + #[test] + fn in_component_filtered_returns_nodes_that_are_unfiltered_for_future_traversals() { + let graph = Graph::new(); + + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 99, 2, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 100, 99, NO_PROPS, Some("B")).unwrap(); + + let result = + in_component_filtered(graph.node(4).unwrap(), GraphFilter.layer("A")).unwrap(); + let result = result + .iter() + .flat_map(|(n, _)| n.id().as_u64()) + .collect::>(); + assert_eq!(result, vec![2]); + let unfiltered_ids: Vec = + in_component_filtered(graph.node(4).unwrap(), GraphFilter.layer("A")) + .unwrap() + .nodes() + .in_neighbours() + .iter() + .flat_map(|(_, ns)| ns.iter().filter_map(|c| c.id().as_u64())) + .collect(); + + assert_eq!(unfiltered_ids, vec![99]); + } + + #[test] + fn in_component_edge_filtered_handles_multiple_inbound_paths_with_distances() { + let graph = Graph::new(); + + graph + .add_edge(1, 1, 2, vec![("p1", Prop::U64(1))], Some("A")) + .unwrap(); + graph + .add_edge(1, 2, 4, vec![("p1", Prop::U64(2))], Some("A")) + .unwrap(); + graph + .add_edge(1, 4, 6, vec![("p1", Prop::U64(3))], Some("A")) + .unwrap(); + graph + .add_edge(1, 3, 4, vec![("p1", Prop::U64(4))], Some("A")) + .unwrap(); + + // Layer B adds an alternate chain to 4 that should be ignored under A filter + graph + .add_edge(1, 10, 11, vec![("p1", Prop::U64(2))], Some("B")) + .unwrap(); + graph + .add_edge(1, 11, 4, vec![("p1", Prop::U64(2))], Some("B")) + .unwrap(); + + let filter = EdgeFilter.layer("A").property("p1").ge(3u64); + check_node_filtered(&graph, 6, filter, vec![(3, 2), (4, 1)]); + } } - #[test] - fn out_component_filtered_returns_nodes_that_are_unfiltered_for_future_traversals() { - let graph = Graph::new(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 99, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); + #[cfg(test)] + mod components_test { + use itertools::Itertools; + use raphtory::{ + algorithms::components::{ + out_component, out_component_filtered, out_components, out_components_filtered, + }, + db::{ + api::mutation::AdditionOps, + graph::views::filter::{ + model::{ + graph_filter::GraphFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, ViewWrapOps, + }, + CreateFilter, + }, + }, + prelude::*, + test_storage, + }; + use std::collections::HashMap; - let unfiltered_ids: Vec = - out_component_filtered(graph.node(1).unwrap(), GraphFilter.layer("A")) + fn check_node(graph: &Graph, node_id: u64, mut correct: Vec<(u64, usize)>) { + let mut results: Vec<_> = out_component(graph.node(node_id).unwrap()) + .iter() + .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) + .collect(); + results.sort(); + correct.sort(); + assert_eq!(results, correct); + } + + fn check_node_filtered( + graph: &Graph, + node_id: u64, + filter: F, + mut correct: Vec<(u64, usize)>, + ) { + let mut results: Vec<_> = out_component_filtered(graph.node(node_id).unwrap(), filter) .unwrap() - .nodes() - .out_neighbours() .iter() - .flat_map(|(_, ns)| ns.iter().filter_map(|c| c.id().as_u64())) + .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) .collect(); + results.sort(); + correct.sort(); + assert_eq!(results, correct); + } - assert_eq!(unfiltered_ids, vec![99]); - } + #[test] + fn out_component_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 1, 3), + (1, 2, 3), + (1, 2, 4), + (1, 2, 5), + (1, 5, 4), + (1, 4, 6), + (1, 4, 7), + (1, 5, 8), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } - #[test] - fn out_component_node_filtered_distances_follow_filtered_graph_only() { - let graph = Graph::new(); + check_node( + &graph, + 1, + vec![(2, 1), (3, 1), (4, 2), (5, 2), (6, 3), (7, 3), (8, 3)], + ); + check_node( + &graph, + 2, + vec![(3, 1), (4, 1), (5, 1), (6, 2), (7, 2), (8, 2)], + ); + check_node(&graph, 3, vec![]); + check_node(&graph, 4, vec![(6, 1), (7, 1)]); + check_node(&graph, 5, vec![(4, 1), (6, 2), (7, 2), (8, 1)]); + check_node(&graph, 6, vec![]); + check_node(&graph, 7, vec![]); + check_node(&graph, 8, vec![]); + } - graph - .add_node(1, 1, vec![("p1", Prop::U64(1))], None, None) - .unwrap(); - graph - .add_node(1, 2, vec![("p1", Prop::U64(2))], None, None) - .unwrap(); - graph - .add_node(1, 3, vec![("p1", Prop::U64(3))], None, None) - .unwrap(); - graph - .add_node(1, 4, vec![("p1", Prop::U64(4))], None, None) - .unwrap(); - - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 1, 3, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); - - graph.add_edge(1, 1, 99, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); - - let filter = NodeFilter.property("p1").ge(3u64); - - check_node_filtered(&graph, 1, filter, vec![(3, 1), (4, 2)]); - } -} + #[test] + fn test_distances() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); + graph.add_edge(0, 5, 3, NO_PROPS, None).unwrap(); -#[cfg(test)] -mod strongly_connected_components_tests { - use itertools::Itertools; - use raphtory::{ - algorithms::components::strongly_connected_components, - prelude::{AdditionOps, Graph, NodeStateGroupBy, NodeStateOps, NodeViewOps, NO_PROPS}, - test_storage, - }; - use std::collections::HashSet; + check_node(&graph, 1, vec![(2, 1), (3, 2), (4, 1), (5, 2)]); + } - #[test] - fn scc_test() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (1, 2, 3), - (1, 2, 5), - (1, 3, 4), - (1, 5, 6), - (1, 6, 4), - (1, 6, 7), - (1, 7, 8), - (1, 8, 6), - (1, 6, 2), - ]; + #[test] + fn out_components_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 1, 3), + (1, 2, 4), + (1, 2, 5), + (1, 5, 4), + (1, 4, 6), + (1, 4, 7), + (1, 5, 8), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + let results = out_components(graph, None); + + let mut correct = HashMap::new(); + correct.insert("1".to_string(), vec![2, 3, 4, 5, 6, 7, 8]); + correct.insert("2".to_string(), vec![4, 5, 6, 7, 8]); + correct.insert("3".to_string(), vec![]); + correct.insert("4".to_string(), vec![6, 7]); + correct.insert("5".to_string(), vec![4, 6, 7, 8]); + correct.insert("6".to_string(), vec![]); + correct.insert("7".to_string(), vec![]); + correct.insert("8".to_string(), vec![]); + let map: HashMap> = results + .iter() + .map(|(k, v)| { + ( + k.name(), + v.out_components + .into_iter() + .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) + .sorted() + .collect(), + ) + }) + .collect(); + assert_eq!(map, correct); + }); } - test_storage!(&graph, |graph| { - let scc_nodes: HashSet> = strongly_connected_components(graph) - .groups() - .into_iter_groups() - .map(|(_, v)| v.name().into_iter_values().sorted().collect()) - .collect(); + #[test] + fn out_component_filtered_by_layer_prunes_cross_layer_paths() { + let graph = Graph::new(); - let expected: HashSet> = [ - vec!["2", "5", "6", "7", "8"], - vec!["1"], - vec!["3"], - vec!["4"], - ] - .into_iter() - .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) - .collect(); - assert_eq!(scc_nodes, expected); - }); - } + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 3, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); - #[test] - fn scc_test_multiple_components() { - let graph = Graph::new(); - let edges = [ - (1, 2), - (2, 3), - (2, 8), - (3, 4), - (3, 7), - (4, 5), - (5, 3), - (5, 6), - (7, 4), - (7, 6), - (8, 1), - (8, 7), - ]; - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + graph.add_edge(1, 2, 10, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 10, 11, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 11, 4, NO_PROPS, Some("B")).unwrap(); + + let filter = GraphFilter.layer("A"); + + check_node_filtered(&graph, 1, filter.clone(), vec![(2, 1), (3, 2), (4, 3)]); + + check_node_filtered(&graph, 2, filter.clone(), vec![(3, 1), (4, 2)]); + + check_node_filtered(&graph, 10, filter.clone(), vec![]); + check_node_filtered(&graph, 11, filter.clone(), vec![]); } - test_storage!(&graph, |graph| { - let scc_nodes: HashSet> = strongly_connected_components(graph) - .groups() - .into_iter_groups() - .map(|(_, v)| v.name().into_iter_values().sorted().collect()) - .collect(); + #[test] + fn out_component_filtered_by_layer_distances_follow_filtered_graph_only() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 1, 3, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + + graph.add_edge(1, 1, 99, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); + + let filter = GraphFilter.layer("A"); - let expected: HashSet> = - [vec!["3", "4", "5", "7"], vec!["1", "2", "8"], vec!["6"]] + check_node_filtered(&graph, 1, filter, vec![(2, 1), (3, 1), (4, 2)]); + } + + #[test] + fn out_components_filtered_by_layer_matches_expected_node_sets() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 3, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 5, NO_PROPS, Some("A")).unwrap(); + + graph.add_edge(1, 5, 100, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 1, 200, NO_PROPS, Some("B")).unwrap(); + + test_storage!(&graph, |graph| { + let results = out_components_filtered(graph, None, GraphFilter.layer("A")).unwrap(); + + let mut correct: HashMap> = HashMap::new(); + correct.insert("1".to_string(), vec![2, 3, 4, 5]); + correct.insert("2".to_string(), vec![3, 4, 5]); + correct.insert("3".to_string(), vec![4]); + correct.insert("4".to_string(), vec![]); + correct.insert("5".to_string(), vec![]); + correct.insert("100".to_string(), vec![]); + correct.insert("200".to_string(), vec![]); + + let map: HashMap> = results .into_iter() - .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) + .map(|(k, v)| { + ( + k.name(), + v.out_components + .into_iter() + .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) + .sorted() + .collect(), + ) + }) .collect(); - assert_eq!(scc_nodes, expected); - }); - } - #[test] - fn scc_test_multiple_components_2() { - let graph = Graph::new(); - let edges = [(1, 2), (1, 3), (1, 4), (4, 2), (3, 4), (2, 3)]; - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + assert_eq!(map, correct); + }); } - test_storage!(&graph, |graph| { - let scc_nodes: HashSet> = strongly_connected_components(graph) - .groups() - .into_iter_groups() - .map(|(_, v)| v.name().into_iter_values().sorted().collect()) - .collect(); + #[test] + fn out_component_filtered_returns_nodes_that_are_unfiltered_for_future_traversals() { + let graph = Graph::new(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 99, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); + + let unfiltered_ids: Vec = + out_component_filtered(graph.node(1).unwrap(), GraphFilter.layer("A")) + .unwrap() + .nodes() + .out_neighbours() + .iter() + .flat_map(|(_, ns)| ns.iter().filter_map(|c| c.id().as_u64())) + .collect(); + + assert_eq!(unfiltered_ids, vec![99]); + } + + #[test] + fn out_component_node_filtered_distances_follow_filtered_graph_only() { + let graph = Graph::new(); + + graph + .add_node(1, 1, vec![("p1", Prop::U64(1))], None, None) + .unwrap(); + graph + .add_node(1, 2, vec![("p1", Prop::U64(2))], None, None) + .unwrap(); + graph + .add_node(1, 3, vec![("p1", Prop::U64(3))], None, None) + .unwrap(); + graph + .add_node(1, 4, vec![("p1", Prop::U64(4))], None, None) + .unwrap(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 1, 3, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + + graph.add_edge(1, 1, 99, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); + + let filter = NodeFilter.property("p1").ge(3u64); + + check_node_filtered(&graph, 1, filter, vec![(3, 1), (4, 2)]); + } + } + + #[cfg(test)] + mod strongly_connected_components_tests { + use itertools::Itertools; + use raphtory::{ + algorithms::components::strongly_connected_components, + prelude::{AdditionOps, Graph, NodeStateGroupBy, NodeStateOps, NodeViewOps, NO_PROPS}, + test_storage, + }; + use std::collections::HashSet; + + #[test] + fn scc_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 2, 3), + (1, 2, 5), + (1, 3, 4), + (1, 5, 6), + (1, 6, 4), + (1, 6, 7), + (1, 7, 8), + (1, 8, 6), + (1, 6, 2), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let scc_nodes: HashSet> = strongly_connected_components(graph) + .groups() + .into_iter_groups() + .map(|(_, v)| v.name().into_iter_values().sorted().collect()) + .collect(); - let expected: HashSet> = [vec!["2", "3", "4"], vec!["1"]] + let expected: HashSet> = [ + vec!["2", "5", "6", "7", "8"], + vec!["1"], + vec!["3"], + vec!["4"], + ] .into_iter() .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) .collect(); - assert_eq!(scc_nodes, expected); - }); - } + assert_eq!(scc_nodes, expected); + }); + } - #[test] - fn scc_test_all_singletons() { - let graph = Graph::new(); - let edges = [ - (0, 1), - (1, 2), - (1, 3), - (2, 4), - (2, 5), - (3, 4), - (3, 5), - (4, 6), - ]; - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + #[test] + fn scc_test_multiple_components() { + let graph = Graph::new(); + let edges = [ + (1, 2), + (2, 3), + (2, 8), + (3, 4), + (3, 7), + (4, 5), + (5, 3), + (5, 6), + (7, 4), + (7, 6), + (8, 1), + (8, 7), + ]; + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let scc_nodes: HashSet> = strongly_connected_components(graph) + .groups() + .into_iter_groups() + .map(|(_, v)| v.name().into_iter_values().sorted().collect()) + .collect(); + + let expected: HashSet> = + [vec!["3", "4", "5", "7"], vec!["1", "2", "8"], vec!["6"]] + .into_iter() + .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) + .collect(); + assert_eq!(scc_nodes, expected); + }); } - test_storage!(&graph, |graph| { - let scc_nodes: HashSet> = strongly_connected_components(graph) - .groups() - .into_iter_groups() - .map(|(_, v)| v.name().into_iter_values().sorted().collect()) - .collect(); + #[test] + fn scc_test_multiple_components_2() { + let graph = Graph::new(); + let edges = [(1, 2), (1, 3), (1, 4), (4, 2), (3, 4), (2, 3)]; + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } - let expected: HashSet> = [ - vec!["0"], - vec!["1"], - vec!["2"], - vec!["3"], - vec!["4"], - vec!["5"], - vec!["6"], - ] - .into_iter() - .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) - .collect(); - assert_eq!(scc_nodes, expected); - }); + test_storage!(&graph, |graph| { + let scc_nodes: HashSet> = strongly_connected_components(graph) + .groups() + .into_iter_groups() + .map(|(_, v)| v.name().into_iter_values().sorted().collect()) + .collect(); + + let expected: HashSet> = [vec!["2", "3", "4"], vec!["1"]] + .into_iter() + .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) + .collect(); + assert_eq!(scc_nodes, expected); + }); + } + + #[test] + fn scc_test_all_singletons() { + let graph = Graph::new(); + let edges = [ + (0, 1), + (1, 2), + (1, 3), + (2, 4), + (2, 5), + (3, 4), + (3, 5), + (4, 6), + ]; + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let scc_nodes: HashSet> = strongly_connected_components(graph) + .groups() + .into_iter_groups() + .map(|(_, v)| v.name().into_iter_values().sorted().collect()) + .collect(); + + let expected: HashSet> = [ + vec!["0"], + vec!["1"], + vec!["2"], + vec!["3"], + vec!["4"], + vec!["5"], + vec!["6"], + ] + .into_iter() + .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) + .collect(); + assert_eq!(scc_nodes, expected); + }); + } } } diff --git a/raphtory/tests/algo_tests/cores.rs b/raphtory/tests/algo_tests/cores.rs index e15a11baff..cdf74a7e95 100644 --- a/raphtory/tests/algo_tests/cores.rs +++ b/raphtory/tests/algo_tests/cores.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod k_core_test { use raphtory::{algorithms::cores::k_core::k_core_set, prelude::*, test_storage}; use std::collections::HashSet; diff --git a/raphtory/tests/algo_tests/embeddings.rs b/raphtory/tests/algo_tests/embeddings.rs index d263804709..26cdea70c4 100644 --- a/raphtory/tests/algo_tests/embeddings.rs +++ b/raphtory/tests/algo_tests/embeddings.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod fast_rp_test { use raphtory::{ diff --git a/raphtory/tests/algo_tests/metrics.rs b/raphtory/tests/algo_tests/metrics.rs index fce5fd54f7..96535adeba 100644 --- a/raphtory/tests/algo_tests/metrics.rs +++ b/raphtory/tests/algo_tests/metrics.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod cc_test { use pretty_assertions::assert_eq; use raphtory::{ @@ -48,7 +48,7 @@ mod cc_test { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod clustering_coefficient_tests { use raphtory::{ algorithms::metrics::clustering_coefficient::{ @@ -121,7 +121,7 @@ mod clustering_coefficient_tests { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod sum_weight_test { use pretty_assertions::assert_eq; use raphtory::{ @@ -203,7 +203,7 @@ mod sum_weight_test { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod degree_test { use raphtory::{ algorithms::metrics::degree::{ @@ -264,7 +264,7 @@ mod degree_test { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod directed_graph_density_tests { use raphtory::{ algorithms::metrics::directed_graph_density::directed_graph_density, @@ -319,7 +319,7 @@ mod directed_graph_density_tests { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod reciprocity_test { use pretty_assertions::assert_eq; use raphtory::{ diff --git a/raphtory/tests/algo_tests/motifs.rs b/raphtory/tests/algo_tests/motifs.rs index abc135a5e6..f18de8c9fd 100644 --- a/raphtory/tests/algo_tests/motifs.rs +++ b/raphtory/tests/algo_tests/motifs.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod global_motifs_test { use raphtory::{ algorithms::motifs::global_temporal_three_node_motifs::temporal_three_node_motif_multi, @@ -66,7 +66,7 @@ mod global_motifs_test { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod local_motifs_test { use raphtory::{ algorithms::motifs::local_temporal_three_node_motifs::temporal_three_node_motif, @@ -376,7 +376,7 @@ mod local_motifs_test { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod triangle_count_tests { use raphtory::{ algorithms::motifs::local_triangle_count::local_triangle_count, @@ -409,7 +409,7 @@ mod triangle_count_tests { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod rich_club_test { use crate::algo_tests::assert_eq_f64; use raphtory::{ @@ -475,7 +475,7 @@ mod rich_club_test { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod triangle_count_tests_alt { use raphtory::{ algorithms::motifs::triangle_count::triangle_count, @@ -575,7 +575,7 @@ mod triangle_count_tests_alt { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod triplet_test { use pretty_assertions::assert_eq; use raphtory::{ diff --git a/raphtory/tests/algo_tests/pathing.rs b/raphtory/tests/algo_tests/pathing.rs index b0dd065dfb..c9c7720a67 100644 --- a/raphtory/tests/algo_tests/pathing.rs +++ b/raphtory/tests/algo_tests/pathing.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod dijkstra_tests { use itertools::Itertools; use raphtory::{ @@ -425,7 +425,7 @@ mod dijkstra_tests { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod sssp_tests { use itertools::Itertools; use raphtory::{ @@ -499,7 +499,7 @@ mod sssp_tests { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-utils"))] mod generic_taint_tests { use raphtory::{ diff --git a/raphtory/tests/cached_view.rs b/raphtory/tests/cached_view.rs index be46bff733..7dcefa302e 100644 --- a/raphtory/tests/cached_view.rs +++ b/raphtory/tests/cached_view.rs @@ -1,375 +1,378 @@ -use itertools::Itertools; -use proptest::prelude::*; -use raphtory::{ - algorithms::motifs::triangle_count::triangle_count, db::graph::graph::assert_graph_equal, - prelude::*, test_storage, -}; -use raphtory_api::core::storage::timeindex::AsTime; - -#[test] -fn empty_graph() { - let graph = Graph::new(); - test_storage!(&graph, |graph| { - let sg = graph.cache_view(); - assert_graph_equal(&sg, &graph); - }); -} +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use proptest::prelude::*; + use raphtory::{ + algorithms::motifs::triangle_count::triangle_count, db::graph::graph::assert_graph_equal, + prelude::*, test_storage, + }; + use raphtory_api::core::storage::timeindex::AsTime; -#[test] -fn empty_window() { - let graph = Graph::new(); - graph.add_edge(1, 1, 1, NO_PROPS, None).unwrap(); - test_storage!(&graph, |graph| { - let window = graph.window(2, 3); - let sg = window.cache_view(); - assert_graph_equal(&window, &sg); - }); -} + #[test] + fn empty_graph() { + let graph = Graph::new(); + test_storage!(&graph, |graph| { + let sg = graph.cache_view(); + assert_graph_equal(&sg, &graph); + }); + } -#[test] -fn test_materialize_no_edges() { - let graph = Graph::new(); + #[test] + fn empty_window() { + let graph = Graph::new(); + graph.add_edge(1, 1, 1, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + let window = graph.window(2, 3); + let sg = window.cache_view(); + assert_graph_equal(&window, &sg); + }); + } - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 2, NO_PROPS, None, None).unwrap(); + #[test] + fn test_materialize_no_edges() { + let graph = Graph::new(); - test_storage!(&graph, |graph| { - let sg = graph.cache_view(); + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 2, NO_PROPS, None, None).unwrap(); - let actual = sg.materialize().unwrap().into_events().unwrap(); - assert_graph_equal(&actual, &sg); - }); -} + test_storage!(&graph, |graph| { + let sg = graph.cache_view(); -#[test] -fn test_mask_the_window_50pc() { - let graph = Graph::new(); - let edges = vec![ - (1, 2, 1), - (1, 3, 2), - (1, 4, 3), - (3, 1, 4), - (3, 4, 5), - (3, 5, 6), - (4, 5, 7), - (5, 6, 8), - (5, 8, 9), - (7, 5, 10), - (8, 5, 11), - (1, 9, 12), - (9, 1, 13), - (6, 3, 14), - (4, 8, 15), - (8, 3, 16), - (5, 10, 17), - (10, 5, 18), - (10, 8, 19), - (1, 11, 20), - (11, 1, 21), - (9, 11, 22), - (11, 9, 23), - ]; - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + let actual = sg.materialize().unwrap().into_events().unwrap(); + assert_graph_equal(&actual, &sg); + }); } - test_storage!(&graph, |graph| { - let window = graph.window(12, 24); - let mask = window.cache_view(); - let ts = triangle_count(&mask, None); - let tg = triangle_count(&window, None); - assert_eq!(ts, tg); - }); -} -#[test] -fn masked_always_equals() { - fn check(edge_list: &[(u8, u8, i16, u8)]) { + #[test] + fn test_mask_the_window_50pc() { let graph = Graph::new(); - for (src, dst, ts, layer) in edge_list { - graph - .add_edge( - *ts as i64, - *src as u64, - *dst as u64, - NO_PROPS, - Some(&layer.to_string()), - ) - .unwrap(); + let edges = vec![ + (1, 2, 1), + (1, 3, 2), + (1, 4, 3), + (3, 1, 4), + (3, 4, 5), + (3, 5, 6), + (4, 5, 7), + (5, 6, 8), + (5, 8, 9), + (7, 5, 10), + (8, 5, 11), + (1, 9, 12), + (9, 1, 13), + (6, 3, 14), + (4, 8, 15), + (8, 3, 16), + (5, 10, 17), + (10, 5, 18), + (10, 8, 19), + (1, 11, 20), + (11, 1, 21), + (9, 11, 22), + (11, 9, 23), + ]; + for (src, dst, ts) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); } - test_storage!(&graph, |graph| { - let layers = graph - .unique_layers() - .take(graph.unique_layers().count() / 2) - .collect_vec(); - - let earliest = graph.earliest_time().unwrap().t(); - let latest = graph.latest_time().unwrap().t(); - let middle = earliest + (latest - earliest) / 2; - - if !layers.is_empty() && earliest < middle && middle < latest { - let subgraph = graph.layers(layers).unwrap().window(earliest, middle); - let masked = subgraph.cache_view(); - assert_graph_equal(&subgraph, &masked); - } + let window = graph.window(12, 24); + let mask = window.cache_view(); + let ts = triangle_count(&mask, None); + let tg = triangle_count(&window, None); + assert_eq!(ts, tg); }); } - proptest!(|(edge_list in any::>().prop_filter("greater than 3",|v| !v.is_empty() ))| { - check(&edge_list); - }) -} - -#[cfg(test)] -mod test_filters_cached_view { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::GraphTransformer, - views::{cached_view::CachedView, window_graph::WindowedGraph}, - }, - }, - prelude::{GraphViewOps, TimeOps}, - }; - use std::ops::Range; - - struct CachedGraphTransformer; + #[test] + fn masked_always_equals() { + fn check(edge_list: &[(u8, u8, i16, u8)]) { + let graph = Graph::new(); + for (src, dst, ts, layer) in edge_list { + graph + .add_edge( + *ts as i64, + *src as u64, + *dst as u64, + NO_PROPS, + Some(&layer.to_string()), + ) + .unwrap(); + } - impl GraphTransformer for CachedGraphTransformer { - type Return = CachedView; - fn apply(&self, graph: G) -> Self::Return { - graph.cache_view() + test_storage!(&graph, |graph| { + let layers = graph + .unique_layers() + .take(graph.unique_layers().count() / 2) + .collect_vec(); + + let earliest = graph.earliest_time().unwrap().t(); + let latest = graph.latest_time().unwrap().t(); + let middle = earliest + (latest - earliest) / 2; + + if !layers.is_empty() && earliest < middle && middle < latest { + let subgraph = graph.layers(layers).unwrap().window(earliest, middle); + let masked = subgraph.cache_view(); + assert_graph_equal(&subgraph, &masked); + } + }); } - } - - struct WindowedCachedGraphTransformer(Range); - impl GraphTransformer for WindowedCachedGraphTransformer { - type Return = WindowedGraph>; - fn apply(&self, graph: G) -> Self::Return { - graph.cache_view().window(self.0.start, self.0.end) - } + proptest!(|(edge_list in any::>().prop_filter("greater than 3",|v| !v.is_empty() ))| { + check(&edge_list); + }) } - mod test_nodes_filters_cached_view_graph { + #[cfg(test)] + mod test_filters_cached_view { use raphtory::{ db::{ api::view::StaticGraphViewOps, graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - TestGraphVariants, TestVariants, - }, - views::filter::model::{ - node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, - }, + assertions::GraphTransformer, + views::{cached_view::CachedView, window_graph::WindowedGraph}, }, }, - prelude::AdditionOps, + prelude::{GraphViewOps, TimeOps}, }; - use raphtory_api::core::entities::properties::prop::Prop; + use std::ops::Range; - use crate::test_filters_cached_view::{ - CachedGraphTransformer, WindowedCachedGraphTransformer, - }; + struct CachedGraphTransformer; - fn init_graph(graph: G) -> G { - let node_data = vec![ - (6, "N1", 2u64, "air_nomad"), - (7, "N1", 1u64, "air_nomad"), - (6, "N2", 1u64, "water_tribe"), - (7, "N2", 2u64, "water_tribe"), - (8, "N3", 1u64, "air_nomad"), - (9, "N4", 1u64, "air_nomad"), - (5, "N5", 1u64, "air_nomad"), - (6, "N5", 2u64, "air_nomad"), - (5, "N6", 1u64, "fire_nation"), - (6, "N6", 1u64, "fire_nation"), - (3, "N7", 1u64, "air_nomad"), - (5, "N7", 1u64, "air_nomad"), - (3, "N8", 1u64, "fire_nation"), - (4, "N8", 2u64, "fire_nation"), - ]; - - for (ts, name, value, kind) in node_data { - graph - .add_node(ts, name, [("p1", Prop::U64(value))], Some(kind), None) - .unwrap(); + impl GraphTransformer for CachedGraphTransformer { + type Return = CachedView; + fn apply(&self, graph: G) -> Self::Return { + graph.cache_view() } - - graph } - #[test] - fn test_nodes_filters() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - CachedGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_graph, - CachedGraphTransformer, - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + struct WindowedCachedGraphTransformer(Range); - #[test] - fn test_nodes_filters_w() { - // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_pg_w() { - let filter = NodeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2", "N5", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + impl GraphTransformer for WindowedCachedGraphTransformer { + type Return = WindowedGraph>; + fn apply(&self, graph: G) -> Self::Return { + graph.cache_view().window(self.0.start, self.0.end) + } } - } - mod test_edges_filter_cached_view_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestVariants, + mod test_nodes_filters_cached_view_graph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::{ + assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, + TestGraphVariants, TestVariants, + }, + views::filter::model::{ + node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, + }, + }, }, - }, - prelude::{AdditionOps, EdgeFilter}, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - use crate::test_filters_cached_view::{ - CachedGraphTransformer, WindowedCachedGraphTransformer, - }; - use raphtory::db::graph::views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }; + prelude::AdditionOps, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + use crate::test::test_filters_cached_view::{ + CachedGraphTransformer, WindowedCachedGraphTransformer, + }; + + fn init_graph(graph: G) -> G { + let node_data = vec![ + (6, "N1", 2u64, "air_nomad"), + (7, "N1", 1u64, "air_nomad"), + (6, "N2", 1u64, "water_tribe"), + (7, "N2", 2u64, "water_tribe"), + (8, "N3", 1u64, "air_nomad"), + (9, "N4", 1u64, "air_nomad"), + (5, "N5", 1u64, "air_nomad"), + (6, "N5", 2u64, "air_nomad"), + (5, "N6", 1u64, "fire_nation"), + (6, "N6", 1u64, "fire_nation"), + (3, "N7", 1u64, "air_nomad"), + (5, "N7", 1u64, "air_nomad"), + (3, "N8", 1u64, "fire_nation"), + (4, "N8", 2u64, "fire_nation"), + ]; + + for (ts, name, value, kind) in node_data { + graph + .add_node(ts, name, [("p1", Prop::U64(value))], Some(kind), None) + .unwrap(); + } - fn init_graph(graph: G) -> G { - let edge_data = vec![ - (6, "N1", "N2", 2u64), - (7, "N1", "N2", 1u64), - (6, "N2", "N3", 1u64), - (7, "N2", "N3", 2u64), - (8, "N3", "N4", 1u64), - (9, "N4", "N5", 1u64), - (5, "N5", "N6", 1u64), - (6, "N5", "N6", 2u64), - (5, "N6", "N7", 1u64), - (6, "N6", "N7", 1u64), - (3, "N7", "N8", 1u64), - (5, "N7", "N8", 1u64), - (3, "N8", "N1", 1u64), - (4, "N8", "N1", 2u64), - ]; - - for (ts, src, dst, p1_val) in edge_data { graph - .add_edge(ts, src, dst, [("p1", Prop::U64(p1_val))], None) - .unwrap(); } - graph - } + #[test] + fn test_nodes_filters() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + CachedGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_graph, + CachedGraphTransformer, + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_filters() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - CachedGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - CachedGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_nodes_filters_w() { + // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_filter_w() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_nodes_filters_pg_w() { + let filter = NodeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2", "N5", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } } - #[test] - fn test_edges_filters_pg_w() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; - assert_filter_edges_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + mod test_edges_filter_cached_view_graph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestVariants, + }, + }, + prelude::{AdditionOps, EdgeFilter}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + use crate::test::test_filters_cached_view::{ + CachedGraphTransformer, WindowedCachedGraphTransformer, + }; + use raphtory::db::graph::views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }; + + fn init_graph(graph: G) -> G { + let edge_data = vec![ + (6, "N1", "N2", 2u64), + (7, "N1", "N2", 1u64), + (6, "N2", "N3", 1u64), + (7, "N2", "N3", 2u64), + (8, "N3", "N4", 1u64), + (9, "N4", "N5", 1u64), + (5, "N5", "N6", 1u64), + (6, "N5", "N6", 2u64), + (5, "N6", "N7", 1u64), + (6, "N6", "N7", 1u64), + (3, "N7", "N8", 1u64), + (5, "N7", "N8", 1u64), + (3, "N8", "N1", 1u64), + (4, "N8", "N1", 2u64), + ]; + + for (ts, src, dst, p1_val) in edge_data { + graph + .add_edge(ts, src, dst, [("p1", Prop::U64(p1_val))], None) + .unwrap(); + } + + graph + } + + #[test] + fn test_edges_filters() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + CachedGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + CachedGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_filter_w() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_w() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; + assert_filter_edges_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } } } } diff --git a/raphtory/tests/db_tests.rs b/raphtory/tests/db_tests.rs index e09a1ff1ef..c0e4e77d1b 100644 --- a/raphtory/tests/db_tests.rs +++ b/raphtory/tests/db_tests.rs @@ -1,3893 +1,3901 @@ -use bigdecimal::BigDecimal; -use chrono::NaiveDateTime; -use itertools::Itertools; -use proptest::{arbitrary::any, prop_assert, prop_assert_eq, proptest, sample::subsequence}; -#[cfg(feature = "proto")] -use raphtory::serialise::StableDecode; -use raphtory::{ - algorithms::{ - centrality::{degree_centrality::degree_centrality, pagerank::unweighted_page_rank}, - components::weakly_connected_components, - }, - db::{ - api::{ - properties::internal::InternalMetadataOps, - state::MergePriority, - view::{ - internal::{GraphTimeSemanticsOps, InternalEdgeFilterOps}, - EdgeViewOps, LayerOps, NodeViewOps, TimeOps, +#[cfg(all(test, feature = "test-utils"))] +mod test { + use bigdecimal::BigDecimal; + use chrono::NaiveDateTime; + use itertools::Itertools; + use proptest::{arbitrary::any, prop_assert, prop_assert_eq, proptest, sample::subsequence}; + #[cfg(feature = "proto")] + use raphtory::serialise::StableDecode; + use raphtory::{ + algorithms::{ + centrality::{degree_centrality::degree_centrality, pagerank::unweighted_page_rank}, + components::weakly_connected_components, + }, + db::{ + api::{ + properties::internal::InternalMetadataOps, + state::MergePriority, + view::{ + internal::{GraphTimeSemanticsOps, InternalEdgeFilterOps}, + EdgeViewOps, LayerOps, NodeViewOps, TimeOps, + }, + }, + graph::{ + edge::EdgeView, edges::Edges, graph::assert_graph_equal, path::PathFromNode, + views::deletion_graph::PersistentGraph, }, }, - graph::{ - edge::EdgeView, edges::Edges, graph::assert_graph_equal, path::PathFromNode, - views::deletion_graph::PersistentGraph, + errors::GraphError, + graphgen::random_attachment::random_attachment, + prelude::*, + test_storage, + test_utils::{ + build_graph, build_graph_strat, EdgeFixture, EdgeUpdatesFixture, GraphFixture, + NodeFixture, PropUpdatesFixture, }, - }, - errors::GraphError, - graphgen::random_attachment::random_attachment, - prelude::*, - test_storage, - test_utils::{ - build_graph, build_graph_strat, EdgeFixture, EdgeUpdatesFixture, GraphFixture, NodeFixture, - PropUpdatesFixture, - }, -}; -use raphtory_api::core::{ - entities::{LayerId, GID, VID}, - storage::{ - arc_str::{ArcStr, OptionAsStr}, - timeindex::{AsTime, EventTime}, - }, - utils::{ - logging::global_info_logger, - time::{ParseTimeError, TryIntoTime, TryIntoTimeNeedsEventId}, - }, -}; -use raphtory_storage::{core_ops::CoreGraphOps, mutation::addition_ops::InternalAdditionOps}; -use rayon::{join, prelude::*}; -use std::{ - collections::{HashMap, HashSet}, - ops::{Deref, Range}, - sync::Arc, -}; -#[cfg(feature = "proto")] -use tempfile::TempDir; -use tracing::{error, info}; - -#[test] -fn edge_metadata() -> Result<(), GraphError> { - let g = Graph::new(); - - g.add_edge(0, 0, 0, NO_PROPS, None)?; - g.add_edge(0, 0, 1, NO_PROPS, None)?; - - g.edge(0, 0).unwrap().update_metadata( - vec![("x".to_string(), Prop::map([("n", Prop::U64(23))]))], - None, - )?; - g.edge(0, 1).unwrap().update_metadata( - vec![( - "a".to_string(), - Prop::map([("a", Prop::U8(1)), ("b", Prop::str("baa"))]), - )], - None, - )?; - - let e1 = g.edge(0, 0).unwrap(); - let actual = e1.metadata().as_map(); - assert_eq!(actual.get("x"), Some(&Prop::map([("n", Prop::U64(23))]))); - - let e2 = g.edge(0, 1).unwrap(); - let actual = e2.metadata().as_vec(); - assert_eq!( - actual, - vec![( - "a".into(), - Prop::map([("b", Prop::str("baa")), ("a", Prop::U8(1))]) - )] - ); - Ok(()) -} + }; + use raphtory_api::core::{ + entities::{LayerId, GID, VID}, + storage::{ + arc_str::{ArcStr, OptionAsStr}, + timeindex::{AsTime, EventTime}, + }, + utils::{ + logging::global_info_logger, + time::{ParseTimeError, TryIntoTime, TryIntoTimeNeedsEventId}, + }, + }; + use raphtory_storage::{core_ops::CoreGraphOps, mutation::addition_ops::InternalAdditionOps}; + use rayon::{join, prelude::*}; + use std::{ + collections::{HashMap, HashSet}, + ops::{Deref, Range}, + sync::Arc, + }; + #[cfg(feature = "proto")] + use tempfile::TempDir; + use tracing::{error, info}; -#[test] -fn test_empty_graph() { - let graph = Graph::new(); - test_storage!(&graph, |graph| { - assert!(!graph.has_edge(1, 2)); + #[test] + fn edge_metadata() -> Result<(), GraphError> { + let g = Graph::new(); - let test_time = 42; - let result = graph.at(test_time); - assert!(result.start.is_some()); - assert!(result.end.is_some()); + g.add_edge(0, 0, 0, NO_PROPS, None)?; + g.add_edge(0, 0, 1, NO_PROPS, None)?; - let result = graph.after(test_time); - assert!(result.start.is_some()); - assert!(result.end.is_none()); + g.edge(0, 0).unwrap().update_metadata( + vec![("x".to_string(), Prop::map([("n", Prop::U64(23))]))], + None, + )?; + g.edge(0, 1).unwrap().update_metadata( + vec![( + "a".to_string(), + Prop::map([("a", Prop::U8(1)), ("b", Prop::str("baa"))]), + )], + None, + )?; - let result = graph.before(test_time); - assert!(result.start.is_none()); - assert!(result.end.is_some()); + let e1 = g.edge(0, 0).unwrap(); + let actual = e1.metadata().as_map(); + assert_eq!(actual.get("x"), Some(&Prop::map([("n", Prop::U64(23))]))); + let e2 = g.edge(0, 1).unwrap(); + let actual = e2.metadata().as_vec(); assert_eq!( - graph.metadata_keys().collect::>(), - Vec::::new() - ); - assert_eq!( - graph.metadata_ids().collect::>(), - Vec::::new() + actual, + vec![( + "a".into(), + Prop::map([("b", Prop::str("baa")), ("a", Prop::U8(1))]) + )] ); - assert_eq!( - graph.metadata_values().collect::>(), - Vec::>::new() - ); - assert!(graph.metadata().get_by_id(1).is_none()); - assert!(graph.get_metadata_id("1").is_none()); - assert!(graph.get_metadata(1).is_none()); - assert_eq!(graph.count_nodes(), 0); - assert_eq!(graph.count_edges(), 0); - assert_eq!(graph.count_temporal_edges(), 0); - - assert!(graph.start().is_none()); - assert!(graph.end().is_none()); - assert_eq!(graph.earliest_time(), None); - // assert!(graph.timeline_end().is_none()); - - assert!(graph.is_empty()); - - assert!(graph.nodes().collect().is_empty()); - assert_eq!(graph.edges().collect(), Vec::>::new()); - assert!(!graph.internal_edge_filtered()); - assert!(graph.edge(1, 2).is_none()); - assert!(graph.latest_time_global().is_none()); - assert!(graph - .latest_time_window(EventTime::start(1), EventTime::start(2)) - .is_none()); - assert!(graph.latest_time().is_none()); - assert!(graph.latest_time_global().is_none()); - assert!(graph.earliest_time_global().is_none()); - }); -} - -#[test] -fn test_multithreaded_add_edge() { - proptest!(|(edges: Vec<(u64, u64)>)| { - let g = Graph::new(); - edges.par_iter().enumerate().for_each(|(t, (i, j))| { - g.add_edge(t as i64, *i, *j, NO_PROPS, None).unwrap(); - }); - prop_assert!(edges.iter().all(|(i, j)| g.has_edge(*i, *j)) && g.count_temporal_edges() == edges.len()); - }); -} + Ok(()) + } -#[test] -fn test_multithreaded_add_edge_both_directions() { - proptest!(|(edges: Vec<(u64, u64)>)| { - let g = Graph::new(); - let mut self_loop_count = 0; - for (src, dst) in edges.iter() { - if src == dst { - self_loop_count += 1; - } - // try to maximise the chance that both directions of the edge are added in parallel - join(|| { - g.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); - }, || {g.add_edge(0, *dst, *src, NO_PROPS, None).unwrap();}); - } + #[test] + fn test_empty_graph() { + let graph = Graph::new(); + test_storage!(&graph, |graph| { + assert!(!graph.has_edge(1, 2)); - prop_assert!(edges.iter().all(|(i, j)| g.has_edge(*i, *j) && g.has_edge(*j, *i)) && g.count_temporal_edges() == 2*edges.len()-self_loop_count); - }); -} + let test_time = 42; + let result = graph.at(test_time); + assert!(result.start.is_some()); + assert!(result.end.is_some()); -#[test] -fn add_node_grows_graph_len() { - proptest!(|(vs: Vec<(i64, u64)>)| { - let g = Graph::new(); + let result = graph.after(test_time); + assert!(result.start.is_some()); + assert!(result.end.is_none()); - let expected_len = vs.iter().map(|(_, v)| v).sorted().dedup().count(); - for (t, v) in vs { - g.add_node(t, v, NO_PROPS, None, None) - .map_err(|err| error!("{:?}", err)) - .ok(); - } + let result = graph.before(test_time); + assert!(result.start.is_none()); + assert!(result.end.is_some()); - prop_assert_eq!(g.count_nodes(), expected_len); - }); -} + assert_eq!( + graph.metadata_keys().collect::>(), + Vec::::new() + ); + assert_eq!( + graph.metadata_ids().collect::>(), + Vec::::new() + ); + assert_eq!( + graph.metadata_values().collect::>(), + Vec::>::new() + ); + assert!(graph.metadata().get_by_id(1).is_none()); + assert!(graph.get_metadata_id("1").is_none()); + assert!(graph.get_metadata(1).is_none()); + assert_eq!(graph.count_nodes(), 0); + assert_eq!(graph.count_edges(), 0); + assert_eq!(graph.count_temporal_edges(), 0); + + assert!(graph.start().is_none()); + assert!(graph.end().is_none()); + assert_eq!(graph.earliest_time(), None); + // assert!(graph.timeline_end().is_none()); + + assert!(graph.is_empty()); + + assert!(graph.nodes().collect().is_empty()); + assert_eq!(graph.edges().collect(), Vec::>::new()); + assert!(!graph.internal_edge_filtered()); + assert!(graph.edge(1, 2).is_none()); + assert!(graph.latest_time_global().is_none()); + assert!(graph + .latest_time_window(EventTime::start(1), EventTime::start(2)) + .is_none()); + assert!(graph.latest_time().is_none()); + assert!(graph.latest_time_global().is_none()); + assert!(graph.earliest_time_global().is_none()); + }); + } -#[test] -fn add_node_gets_names() { - proptest!(|(vs: Vec)| { - global_info_logger(); - let g = Graph::new(); + #[test] + fn test_multithreaded_add_edge() { + proptest!(|(edges: Vec<(u64, u64)>)| { + let g = Graph::new(); + edges.par_iter().enumerate().for_each(|(t, (i, j))| { + g.add_edge(t as i64, *i, *j, NO_PROPS, None).unwrap(); + }); + prop_assert!(edges.iter().all(|(i, j)| g.has_edge(*i, *j)) && g.count_temporal_edges() == edges.len()); + }); + } - let expected_len = vs.iter().sorted().dedup().count(); - for (t, name) in vs.iter().enumerate() { - g.add_node(t as i64, name.clone(), NO_PROPS, None, None) - .map_err(|err| info!("{:?}", err)) - .ok(); - } + #[test] + fn test_multithreaded_add_edge_both_directions() { + proptest!(|(edges: Vec<(u64, u64)>)| { + let g = Graph::new(); + let mut self_loop_count = 0; + for (src, dst) in edges.iter() { + if src == dst { + self_loop_count += 1; + } + // try to maximise the chance that both directions of the edge are added in parallel + join(|| { + g.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); + }, || {g.add_edge(0, *dst, *src, NO_PROPS, None).unwrap();}); + } - prop_assert_eq!(g.count_nodes(), expected_len); + prop_assert!(edges.iter().all(|(i, j)| g.has_edge(*i, *j) && g.has_edge(*j, *i)) && g.count_temporal_edges() == 2*edges.len()-self_loop_count); + }); + } - let res = vs.iter().all(|name| { - let v = g.node(name.clone()).unwrap(); - v.name() == name.clone() - }); - prop_assert!(res); - }); -} + #[test] + fn add_node_grows_graph_len() { + proptest!(|(vs: Vec<(i64, u64)>)| { + let g = Graph::new(); -#[test] -fn add_edge_grows_graph_edge_len() { - proptest!(|(edges: Vec<(i64, u64, u64)>)| { - let g = Graph::new(); + let expected_len = vs.iter().map(|(_, v)| v).sorted().dedup().count(); + for (t, v) in vs { + g.add_node(t, v, NO_PROPS, None, None) + .map_err(|err| error!("{:?}", err)) + .ok(); + } - let unique_nodes_count = edges - .iter() - .flat_map(|(_, src, dst)| vec![src, dst]) - .sorted() - .dedup() - .count(); + prop_assert_eq!(g.count_nodes(), expected_len); + }); + } - let unique_edge_count = edges - .iter() - .map(|(_, src, dst)| (src, dst)) - .unique() - .count(); + #[test] + fn add_node_gets_names() { + proptest!(|(vs: Vec)| { + global_info_logger(); + let g = Graph::new(); + + let expected_len = vs.iter().sorted().dedup().count(); + for (t, name) in vs.iter().enumerate() { + g.add_node(t as i64, name.clone(), NO_PROPS, None, None) + .map_err(|err| info!("{:?}", err)) + .ok(); + } - for (t, src, dst) in edges { - g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } + prop_assert_eq!(g.count_nodes(), expected_len); - prop_assert_eq!(g.count_nodes(), unique_nodes_count); - prop_assert_eq!(g.count_edges(), unique_edge_count); - }); -} + let res = vs.iter().all(|name| { + let v = g.node(name.clone()).unwrap(); + v.name() == name.clone() + }); + prop_assert!(res); + }); + } -#[test] -fn simple_add_edge() { - let edges = vec![(1, 1, 2), (2, 2, 3), (3, 3, 4)]; + #[test] + fn add_edge_grows_graph_edge_len() { + proptest!(|(edges: Vec<(i64, u64, u64)>)| { + let g = Graph::new(); + + let unique_nodes_count = edges + .iter() + .flat_map(|(_, src, dst)| vec![src, dst]) + .sorted() + .dedup() + .count(); + + let unique_edge_count = edges + .iter() + .map(|(_, src, dst)| (src, dst)) + .unique() + .count(); + + for (t, src, dst) in edges { + g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } - let g = Graph::new(); - for &(t, src, dst) in edges.iter() { - g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + prop_assert_eq!(g.count_nodes(), unique_nodes_count); + prop_assert_eq!(g.count_edges(), unique_edge_count); + }); } - assert!(edges.iter().all(|&(_, src, dst)| g.has_edge(src, dst))) -} + #[test] + fn simple_add_edge() { + let edges = vec![(1, 1, 2), (2, 2, 3), (3, 3, 4)]; -#[test] -fn add_edge_works() { - proptest!(|(edges: Vec<(i64, u64, u64)>)| { let g = Graph::new(); for &(t, src, dst) in edges.iter() { g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); } - prop_assert!(edges.iter().all(|&(_, src, dst)| g.has_edge(src, dst))); - }); -} + assert!(edges.iter().all(|&(_, src, dst)| g.has_edge(src, dst))) + } -#[test] -fn get_edge_works() { - proptest!(|(edges: Vec<(i64, u64, u64)>)| { - let g = Graph::new(); - for &(t, src, dst) in edges.iter() { - g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } + #[test] + fn add_edge_works() { + proptest!(|(edges: Vec<(i64, u64, u64)>)| { + let g = Graph::new(); + for &(t, src, dst) in edges.iter() { + g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } - prop_assert!(edges - .iter() - .all(|&(_, src, dst)| g.edge(src, dst).is_some())); - }); -} + prop_assert!(edges.iter().all(|&(_, src, dst)| g.has_edge(src, dst))); + }); + } -#[test] -fn import_from_another_graph() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); + #[test] + fn get_edge_works() { + proptest!(|(edges: Vec<(i64, u64, u64)>)| { + let g = Graph::new(); + for &(t, src, dst) in edges.iter() { + g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } - assert_eq!(g_b.history(), vec![1]); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let gg = Graph::new(); - let res = gg.import_node(&g_a, false).unwrap(); - assert_eq!(res.name(), "A"); - assert_eq!(res.history(), vec![0]); - let res = gg.import_node(&g_b, false).unwrap(); - assert_eq!(res.name(), "B"); - assert_eq!(res.history(), vec![1]); - assert_eq!(res.properties().get("temp").unwrap(), Prop::Bool(true)); - assert_eq!(res.metadata().get("con").unwrap(), Prop::I64(11)); - - let gg = Graph::new(); - gg.add_node(1, "B", NO_PROPS, None, None).unwrap(); - let res = gg.import_nodes(vec![&g_a, &g_b], false); - match res { - Err(GraphError::NodesExistError(ids)) => { - assert_eq!( - ids.into_iter() - .map(|id| id.to_string()) - .collect::>(), - vec!["B"], - ); - } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), + prop_assert!(edges + .iter() + .all(|&(_, src, dst)| g.edge(src, dst).is_some())); + }); } - assert_eq!(gg.node("A"), None); + #[test] + fn import_from_another_graph() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); - let gg = Graph::new(); - gg.import_nodes(vec![&g_a, &g_b], false).unwrap(); - assert_eq!(gg.nodes().name().collect_vec(), vec!["A", "B"]); + assert_eq!(g_b.history(), vec![1]); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + let gg = Graph::new(); + let res = gg.import_node(&g_a, false).unwrap(); + assert_eq!(res.name(), "A"); + assert_eq!(res.history(), vec![0]); + let res = gg.import_node(&g_b, false).unwrap(); + assert_eq!(res.name(), "B"); + assert_eq!(res.history(), vec![1]); + assert_eq!(res.properties().get("temp").unwrap(), Prop::Bool(true)); + assert_eq!(res.metadata().get("con").unwrap(), Prop::I64(11)); + + let gg = Graph::new(); + gg.add_node(1, "B", NO_PROPS, None, None).unwrap(); + let res = gg.import_nodes(vec![&g_a, &g_b], false); + match res { + Err(GraphError::NodesExistError(ids)) => { + assert_eq!( + ids.into_iter() + .map(|id| id.to_string()) + .collect::>(), + vec!["B"], + ); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), + } - let e_a_b = g.add_edge(2, "A", "B", NO_PROPS, None).unwrap(); - let res = gg.import_edge(&e_a_b, false).unwrap(); - assert_eq!( - (res.src().name(), res.dst().name()), - (e_a_b.src().name(), e_a_b.dst().name()) - ); - let e_a_b_p = g - .add_edge( - 3, - "A", - "B", - vec![("etemp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); - let gg = Graph::new(); - let _ = gg.add_node(0, "B", NO_PROPS, None, None); - let res = gg.import_edge(&e_a_b_p, false).expect("Failed to add edge"); - assert_eq!(res.properties().as_vec(), e_a_b_p.properties().as_vec()); - - let e_c_d = g.add_edge(4, "C", "D", NO_PROPS, None).unwrap(); - - let gg = Graph::new(); - gg.import_edges(vec![&e_a_b, &e_c_d], false).unwrap(); - assert_eq!(gg.edges().len(), 2); - - let gg = Graph::new(); - gg.add_edge(1, "C", "D", NO_PROPS, None).unwrap(); - let res = gg.import_edges(vec![&e_a_b, &e_c_d], false); - match res { - Err(GraphError::EdgesExistError(duplicates)) => { - assert_eq!( - duplicates - .into_iter() - .map(|(x, y)| (x.to_string(), y.to_string())) - .collect::>(), - vec![("C".to_string(), "D".to_string())] - ); + assert_eq!(gg.node("A"), None); + + let gg = Graph::new(); + gg.import_nodes(vec![&g_a, &g_b], false).unwrap(); + assert_eq!(gg.nodes().name().collect_vec(), vec!["A", "B"]); + + let e_a_b = g.add_edge(2, "A", "B", NO_PROPS, None).unwrap(); + let res = gg.import_edge(&e_a_b, false).unwrap(); + assert_eq!( + (res.src().name(), res.dst().name()), + (e_a_b.src().name(), e_a_b.dst().name()) + ); + let e_a_b_p = g + .add_edge( + 3, + "A", + "B", + vec![("etemp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); + let gg = Graph::new(); + let _ = gg.add_node(0, "B", NO_PROPS, None, None); + let res = gg.import_edge(&e_a_b_p, false).expect("Failed to add edge"); + assert_eq!(res.properties().as_vec(), e_a_b_p.properties().as_vec()); + + let e_c_d = g.add_edge(4, "C", "D", NO_PROPS, None).unwrap(); + + let gg = Graph::new(); + gg.import_edges(vec![&e_a_b, &e_c_d], false).unwrap(); + assert_eq!(gg.edges().len(), 2); + + let gg = Graph::new(); + gg.add_edge(1, "C", "D", NO_PROPS, None).unwrap(); + let res = gg.import_edges(vec![&e_a_b, &e_c_d], false); + match res { + Err(GraphError::EdgesExistError(duplicates)) => { + assert_eq!( + duplicates + .into_iter() + .map(|(x, y)| (x.to_string(), y.to_string())) + .collect::>(), + vec![("C".to_string(), "D".to_string())] + ); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), + assert_eq!(gg.edge("A", "B"), None); } - assert_eq!(gg.edge("A", "B"), None); -} -#[test] -fn import_node_as() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - - let gg = Graph::new(); - let res = gg.import_node_as(&g_a, "X", false).unwrap(); - assert_eq!(res.name(), "X"); - assert_eq!(res.history(), vec![0]); - - let _ = gg.add_node(1, "Y", NO_PROPS, None, None).unwrap(); - let res = gg.import_node_as(&g_b, "Y", false); - match res { - Err(GraphError::NodeExistsError(id)) => { - assert_eq!(id.to_string(), "Y"); + #[test] + fn import_node_as() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + + let gg = Graph::new(); + let res = gg.import_node_as(&g_a, "X", false).unwrap(); + assert_eq!(res.name(), "X"); + assert_eq!(res.history(), vec![0]); + + let _ = gg.add_node(1, "Y", NO_PROPS, None, None).unwrap(); + let res = gg.import_node_as(&g_b, "Y", false); + match res { + Err(GraphError::NodeExistsError(id)) => { + assert_eq!(id.to_string(), "Y"); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["X", "Y"]); // Nodes up until first failure are imported - let y = gg.node("Y").unwrap(); + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["X", "Y"]); // Nodes up until first failure are imported + let y = gg.node("Y").unwrap(); - assert_eq!(y.name(), "Y"); - assert_eq!(y.history().t(), vec![1]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); -} + assert_eq!(y.name(), "Y"); + assert_eq!(y.history().t(), vec![1]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); + } -#[test] -fn import_node_as_merge() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + #[test] + fn import_node_as_merge() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let gg = Graph::new(); - gg.add_node(1, "Y", NO_PROPS, None, None).unwrap(); + let gg = Graph::new(); + gg.add_node(1, "Y", NO_PROPS, None, None).unwrap(); - let res = gg.import_node_as(&g_a, "X", false).unwrap(); - assert_eq!(res.name(), "X"); - assert_eq!(res.history(), vec![0]); + let res = gg.import_node_as(&g_a, "X", false).unwrap(); + assert_eq!(res.name(), "X"); + assert_eq!(res.history(), vec![0]); - let res = gg.import_node_as(&g_b, "Y", true).unwrap(); - assert_eq!(res.name(), "Y"); - assert_eq!(res.history(), vec![1, 1]); - assert_eq!(res.properties().get("temp").unwrap(), Prop::Bool(true)); - assert_eq!(res.metadata().get("con").unwrap(), Prop::I64(11)); -} + let res = gg.import_node_as(&g_b, "Y", true).unwrap(); + assert_eq!(res.name(), "Y"); + assert_eq!(res.history(), vec![1, 1]); + assert_eq!(res.properties().get("temp").unwrap(), Prop::Bool(true)); + assert_eq!(res.metadata().get("con").unwrap(), Prop::I64(11)); + } -#[test] -fn import_nodes_as() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let g_c = g.add_node(0, "C", NO_PROPS, None, None).unwrap(); - - let gg = Graph::new(); - gg.add_node(1, "Q", NO_PROPS, None, None).unwrap(); - gg.add_node(1, "R", NO_PROPS, None, None).unwrap(); - let res = gg.import_nodes_as(vec![&g_a, &g_b, &g_c], vec!["P", "Q", "R"], false); - match res { - Err(GraphError::NodesExistError(ids)) => { - assert_eq!( - ids.into_iter() - .map(|id| id.to_string()) - .collect::>(), - vec!["Q", "R"], - ); + #[test] + fn import_nodes_as() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + let g_c = g.add_node(0, "C", NO_PROPS, None, None).unwrap(); + + let gg = Graph::new(); + gg.add_node(1, "Q", NO_PROPS, None, None).unwrap(); + gg.add_node(1, "R", NO_PROPS, None, None).unwrap(); + let res = gg.import_nodes_as(vec![&g_a, &g_b, &g_c], vec!["P", "Q", "R"], false); + match res { + Err(GraphError::NodesExistError(ids)) => { + assert_eq!( + ids.into_iter() + .map(|id| id.to_string()) + .collect::>(), + vec!["Q", "R"], + ); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["Q", "R"]); // Nodes up until first failure are imported - let y = gg.node("Q").unwrap(); - assert_eq!(y.name(), "Q"); - assert_eq!(y.history(), vec![1]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); -} + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["Q", "R"]); // Nodes up until first failure are imported + let y = gg.node("Q").unwrap(); + assert_eq!(y.name(), "Q"); + assert_eq!(y.history(), vec![1]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); + } -#[test] -fn import_nodes_as_merge() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + #[test] + fn import_nodes_as_merge() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let gg = Graph::new(); - gg.add_node(1, "Q", NO_PROPS, None, None).unwrap(); - gg.import_nodes_as(vec![&g_a, &g_b], vec!["P", "Q"], true) - .unwrap(); - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["P", "Q"]); - let y = gg.node("Q").unwrap(); - assert_eq!(y.name(), "Q"); - assert_eq!(y.history().t().collect(), vec![1, 1]); - assert_eq!(y.properties().get("temp").unwrap(), Prop::Bool(true)); - assert_eq!(y.metadata().get("con").unwrap(), Prop::I64(11)); -} + let gg = Graph::new(); + gg.add_node(1, "Q", NO_PROPS, None, None).unwrap(); + gg.import_nodes_as(vec![&g_a, &g_b], vec!["P", "Q"], true) + .unwrap(); + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["P", "Q"]); + let y = gg.node("Q").unwrap(); + assert_eq!(y.name(), "Q"); + assert_eq!(y.history().t().collect(), vec![1, 1]); + assert_eq!(y.properties().get("temp").unwrap(), Prop::Bool(true)); + assert_eq!(y.metadata().get("con").unwrap(), Prop::I64(11)); + } -#[test] -fn import_edge_as() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]) - .unwrap(); - let e_a_b = g - .add_edge( - 2, - "A", - "B", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); - let e_b_c = g - .add_edge( - 2, - "B", - "C", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); + #[test] + fn import_edge_as() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]) + .unwrap(); + let e_a_b = g + .add_edge( + 2, + "A", + "B", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); + let e_b_c = g + .add_edge( + 2, + "B", + "C", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); - let gg = Graph::new(); - gg.add_edge(1, "X", "Y", NO_PROPS, None).unwrap(); - gg.import_edge_as(&e_b_c, ("Y", "Z"), false).unwrap(); - let res = gg.import_edge_as(&e_a_b, ("X", "Y"), false); - match res { - Err(GraphError::EdgeExistsError(src_id, dst_id)) => { - assert_eq!(src_id.to_string(), "X"); - assert_eq!(dst_id.to_string(), "Y"); + let gg = Graph::new(); + gg.add_edge(1, "X", "Y", NO_PROPS, None).unwrap(); + gg.import_edge_as(&e_b_c, ("Y", "Z"), false).unwrap(); + let res = gg.import_edge_as(&e_a_b, ("X", "Y"), false); + match res { + Err(GraphError::EdgeExistsError(src_id, dst_id)) => { + assert_eq!(src_id.to_string(), "X"); + assert_eq!(dst_id.to_string(), "Y"); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["X", "Y", "Z"]); - let x = gg.node("X").unwrap(); - assert_eq!(x.name(), "X"); - assert_eq!(x.history(), vec![1]); - let y = gg.node("Y").unwrap(); - assert_eq!(y.name(), "Y"); - assert_eq!(y.history(), vec![1, 2]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); - - let e_src = gg.edge("X", "Y").unwrap().src().name(); - let e_dst = gg.edge("X", "Y").unwrap().dst().name(); - assert_eq!(e_src, "X"); - assert_eq!(e_dst, "Y"); - - let props = gg.edge("X", "Y").unwrap().properties().as_vec(); - assert_eq!(props, vec![]); -} - -#[test] -fn import_edge_as_merge() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let e_a_b = g - .add_edge( - 2, - "A", - "B", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["X", "Y", "Z"]); + let x = gg.node("X").unwrap(); + assert_eq!(x.name(), "X"); + assert_eq!(x.history(), vec![1]); + let y = gg.node("Y").unwrap(); + assert_eq!(y.name(), "Y"); + assert_eq!(y.history(), vec![1, 2]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); + + let e_src = gg.edge("X", "Y").unwrap().src().name(); + let e_dst = gg.edge("X", "Y").unwrap().dst().name(); + assert_eq!(e_src, "X"); + assert_eq!(e_dst, "Y"); + + let props = gg.edge("X", "Y").unwrap().properties().as_vec(); + assert_eq!(props, vec![]); + } - let gg = Graph::new(); - let _ = gg.add_edge(3, "X", "Y", NO_PROPS, None).unwrap(); - let res = gg.import_edge_as(&e_a_b, ("X", "Y"), true).unwrap(); - assert_eq!(res.src().name(), "X"); - assert_eq!(res.dst().name(), "Y"); - assert_eq!(res.properties().as_vec(), e_a_b.properties().as_vec()); - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["X", "Y"]); - let x = gg.node("X").unwrap(); - assert_eq!(x.name(), "X"); - assert_eq!(x.history(), vec![2, 3]); - let y = gg.node("Y").unwrap(); - assert_eq!(y.name(), "Y"); - assert_eq!(y.history(), vec![2, 3]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); -} + #[test] + fn import_edge_as_merge() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + let e_a_b = g + .add_edge( + 2, + "A", + "B", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); -#[test] -fn import_edges_as() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]) - .unwrap(); - g.add_node(0, "C", NO_PROPS, None, None).unwrap(); - let e_a_b = g - .add_edge( - 2, - "A", - "B", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); - let e_b_c = g.add_edge(2, "B", "C", NO_PROPS, None).unwrap(); + let gg = Graph::new(); + let _ = gg.add_edge(3, "X", "Y", NO_PROPS, None).unwrap(); + let res = gg.import_edge_as(&e_a_b, ("X", "Y"), true).unwrap(); + assert_eq!(res.src().name(), "X"); + assert_eq!(res.dst().name(), "Y"); + assert_eq!(res.properties().as_vec(), e_a_b.properties().as_vec()); + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["X", "Y"]); + let x = gg.node("X").unwrap(); + assert_eq!(x.name(), "X"); + assert_eq!(x.history(), vec![2, 3]); + let y = gg.node("Y").unwrap(); + assert_eq!(y.name(), "Y"); + assert_eq!(y.history(), vec![2, 3]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); + } - let gg = Graph::new(); - gg.add_edge(1, "Y", "Z", NO_PROPS, None).unwrap(); - let res = gg.import_edges_as([&e_a_b, &e_b_c], [("X", "Y"), ("Y", "Z")], false); - match res { - Err(GraphError::EdgesExistError(duplicates)) => { - assert_eq!( - duplicates - .into_iter() - .map(|(x, y)| (x.to_string(), y.to_string())) - .collect::>(), - vec![("Y".to_string(), "Z".to_string())] - ); + #[test] + fn import_edges_as() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]) + .unwrap(); + g.add_node(0, "C", NO_PROPS, None, None).unwrap(); + let e_a_b = g + .add_edge( + 2, + "A", + "B", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); + let e_b_c = g.add_edge(2, "B", "C", NO_PROPS, None).unwrap(); + + let gg = Graph::new(); + gg.add_edge(1, "Y", "Z", NO_PROPS, None).unwrap(); + let res = gg.import_edges_as([&e_a_b, &e_b_c], [("X", "Y"), ("Y", "Z")], false); + match res { + Err(GraphError::EdgesExistError(duplicates)) => { + assert_eq!( + duplicates + .into_iter() + .map(|(x, y)| (x.to_string(), y.to_string())) + .collect::>(), + vec![("Y".to_string(), "Z".to_string())] + ); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["Y", "Z"]); - let y = gg.node("Y").unwrap(); - assert_eq!(y.name(), "Y"); - assert_eq!(y.history(), vec![1]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); - let x = gg.node("Z").unwrap(); - assert_eq!(x.name(), "Z"); - assert_eq!(x.history(), vec![1]); - - assert!(gg.edge("X", "Y").is_none()); - - let e_y_z = gg.edge("Y", "Z").unwrap(); - assert_eq!( - (e_y_z.src().name().as_str(), e_y_z.dst().name().as_str()), - ("Y", "Z") - ); - - let props = e_y_z.properties().as_vec(); - assert_eq!(props, vec![]); -} - -#[test] -fn import_edges_as_merge() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let e_a_b = g - .add_edge( - 2, - "A", - "B", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["Y", "Z"]); + let y = gg.node("Y").unwrap(); + assert_eq!(y.name(), "Y"); + assert_eq!(y.history(), vec![1]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); + let x = gg.node("Z").unwrap(); + assert_eq!(x.name(), "Z"); + assert_eq!(x.history(), vec![1]); + + assert!(gg.edge("X", "Y").is_none()); + + let e_y_z = gg.edge("Y", "Z").unwrap(); + assert_eq!( + (e_y_z.src().name().as_str(), e_y_z.dst().name().as_str()), + ("Y", "Z") + ); - let gg = Graph::new(); - gg.add_edge(3, "X", "Y", NO_PROPS, None).unwrap(); - gg.import_edges_as([&e_a_b], [("X", "Y")], true).unwrap(); - - let e_x_y = gg.edge("X", "Y").unwrap(); - assert_eq!( - (e_x_y.src().name().as_str(), e_x_y.dst().name().as_str()), - ("X", "Y") - ); - assert_eq!(e_x_y.properties().get("e_temp").unwrap(), Prop::Bool(false)); - - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["X", "Y"]); - let x = gg.node("X").unwrap(); - assert_eq!(x.name(), "X"); - assert_eq!(x.history(), vec![2, 3]); - let y = gg.node("Y").unwrap(); - assert_eq!(y.name(), "Y"); - assert_eq!(y.history(), vec![2, 3]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); -} + let props = e_y_z.properties().as_vec(); + assert_eq!(props, vec![]); + } -#[test] -fn props_with_layers() { - global_info_logger(); - let g = Graph::new(); - g.add_edge(0, "A", "B", NO_PROPS, None).unwrap(); - let ed = g.edge("A", "B").unwrap(); - ed.add_metadata(vec![("CCC", Prop::str("RED"))], None) - .unwrap(); - info!("{:?}", ed.metadata().as_map()); - g.add_edge(0, "A", "B", NO_PROPS, Some("LAYERONE")).unwrap(); - ed.add_metadata(vec![("CCC", Prop::str("BLUE"))], Some("LAYERONE")) - .unwrap(); - info!("{:?}", ed.metadata().as_map()); -} + #[test] + fn import_edges_as_merge() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + let e_a_b = g + .add_edge( + 2, + "A", + "B", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); -#[test] -#[cfg(feature = "proto")] -fn graph_save_to_load_from_file() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; + let gg = Graph::new(); + gg.add_edge(3, "X", "Y", NO_PROPS, None).unwrap(); + gg.import_edges_as([&e_a_b], [("X", "Y")], true).unwrap(); - let g = Graph::new(); + let e_x_y = gg.edge("X", "Y").unwrap(); + assert_eq!( + (e_x_y.src().name().as_str(), e_x_y.dst().name().as_str()), + ("X", "Y") + ); + assert_eq!(e_x_y.properties().get("e_temp").unwrap(), Prop::Bool(false)); + + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["X", "Y"]); + let x = gg.node("X").unwrap(); + assert_eq!(x.name(), "X"); + assert_eq!(x.history(), vec![2, 3]); + let y = gg.node("Y").unwrap(); + assert_eq!(y.name(), "Y"); + assert_eq!(y.history(), vec![2, 3]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); + } - for (t, src, dst) in &vs { - g.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + #[test] + fn props_with_layers() { + global_info_logger(); + let g = Graph::new(); + g.add_edge(0, "A", "B", NO_PROPS, None).unwrap(); + let ed = g.edge("A", "B").unwrap(); + ed.add_metadata(vec![("CCC", Prop::str("RED"))], None) + .unwrap(); + info!("{:?}", ed.metadata().as_map()); + g.add_edge(0, "A", "B", NO_PROPS, Some("LAYERONE")).unwrap(); + ed.add_metadata(vec![("CCC", Prop::str("BLUE"))], Some("LAYERONE")) + .unwrap(); + info!("{:?}", ed.metadata().as_map()); } - let tmp_raphtory_path: TempDir = TempDir::new().unwrap(); + #[test] + #[cfg(feature = "proto")] + fn graph_save_to_load_from_file() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; - let graph_path = format!("{}/graph.bin", tmp_raphtory_path.path().display()); - g.encode(&graph_path).unwrap(); + let g = Graph::new(); - // Load from files - let g2 = Graph::decode(&graph_path).unwrap(); + for (t, src, dst) in &vs { + g.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } - assert_eq!(g, g2); -} + let tmp_raphtory_path: TempDir = TempDir::new().unwrap(); -#[test] -fn has_edge() { - let g = Graph::new(); - g.add_edge(1, 7, 8, NO_PROPS, None).unwrap(); + let graph_path = format!("{}/graph.bin", tmp_raphtory_path.path().display()); + g.encode(&graph_path).unwrap(); - assert!(!g.has_edge(8, 7)); - assert!(g.has_edge(7, 8)); + // Load from files + let g2 = Graph::decode(&graph_path).unwrap(); - g.add_edge(1, 7, 9, NO_PROPS, None).unwrap(); - - assert!(!g.has_edge(9, 7)); - assert!(g.has_edge(7, 9)); -} - -#[test] -fn has_edge_str() { - let g = Graph::new(); - g.add_edge(2, "haaroon", "northLondon", NO_PROPS, None) - .unwrap(); - assert!(g.has_edge("haaroon", "northLondon")); -} + assert_eq!(g, g2); + } -#[test] -fn graph_edge() { - let graph = Graph::new(); - let es = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - for (t, src, dst) in es { - graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let e = graph - .window(i64::MIN, i64::MAX) - .layers(Layer::Default) - .unwrap() - .edge(1, 3) - .unwrap(); + #[test] + fn has_edge() { + let g = Graph::new(); + g.add_edge(1, 7, 8, NO_PROPS, None).unwrap(); - assert_eq!(e.src().id().into_u64(), Some(1u64)); - assert_eq!(e.dst().id().into_u64(), Some(3u64)); - }); -} + assert!(!g.has_edge(8, 7)); + assert!(g.has_edge(7, 8)); -#[test] -fn graph_degree_window() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; + g.add_edge(1, 7, 9, NO_PROPS, None).unwrap(); - let graph = Graph::new(); + assert!(!g.has_edge(9, 7)); + assert!(g.has_edge(7, 9)); + } - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + #[test] + fn has_edge_str() { + let g = Graph::new(); + g.add_edge(2, "haaroon", "northLondon", NO_PROPS, None) + .unwrap(); + assert!(g.has_edge("haaroon", "northLondon")); } - let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; - let actual = (1..=3) - .map(|i| { - let v = graph.node(i).unwrap(); - ( - v.window(-1, 7).in_degree(), - v.window(1, 7).out_degree(), - v.window(0, 1).degree(), - ) - }) - .collect::>(); + #[test] + fn graph_edge() { + let graph = Graph::new(); + let es = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + for (t, src, dst) in es { + graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } - assert_eq!(actual, expected); -} + test_storage!(&graph, |graph| { + let e = graph + .window(i64::MIN, i64::MAX) + .layers(Layer::Default) + .unwrap() + .edge(1, 3) + .unwrap(); -#[test] -fn graph_edges_window() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } - let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; - let actual = (1..=3) - .map(|i| { - let v = graph.node(i).unwrap(); - ( - v.window(-1, 7).in_edges().iter().count(), - v.window(1, 7).out_edges().iter().count(), - v.window(0, 1).edges().iter().count(), - ) - }) - .collect::>(); + assert_eq!(e.src().id().into_u64(), Some(1u64)); + assert_eq!(e.dst().id().into_u64(), Some(3u64)); + }); + } - assert_eq!(actual, expected); -} + #[test] + fn graph_degree_window() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; -#[test] -fn test_explode_layers_time() { - global_info_logger(); - let g = Graph::new(); - g.add_edge( - 1, - 1, - 2, - vec![("duration".to_string(), Prop::U32(5))], - Some("a"), - ) - .map_err(|err| error!("{:?}", err)) - .ok(); - g.add_edge( - 2, - 1, - 2, - vec![("duration".to_string(), Prop::U32(5))], - Some("a"), - ) - .map_err(|err| error!("{:?}", err)) - .ok(); - g.add_edge( - 3, - 1, - 2, - vec![("duration".to_string(), Prop::U32(5))], - Some("a"), - ) - .map_err(|err| error!("{:?}", err)) - .ok(); - g.add_edge( - 4, - 1, - 2, - vec![("duration".to_string(), Prop::U32(6))], - Some("b"), - ) - .map_err(|err| error!("{:?}", err)) - .ok(); - g.add_edge(5, 1, 2, NO_PROPS, Some("c")) - .map_err(|err| error!("{:?}", err)) - .ok(); + let graph = Graph::new(); - assert_eq!(g.latest_time().unwrap().t(), 5); + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } - let earliest_times = g - .edge(1, 2) - .unwrap() - .explode_layers() - .earliest_time() - .map(|t| t.unwrap().t()) - .collect_vec(); + let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; + let actual = (1..=3) + .map(|i| { + let v = graph.node(i).unwrap(); + ( + v.window(-1, 7).in_degree(), + v.window(1, 7).out_degree(), + v.window(0, 1).degree(), + ) + }) + .collect::>(); - assert_eq!(earliest_times, vec![1, 4, 5]); + assert_eq!(actual, expected); + } - let latest_times = g - .edge(1, 2) - .unwrap() - .explode_layers() - .latest_time() - .map(|t| t.unwrap().t()) - .collect_vec(); + #[test] + fn graph_edges_window() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; - assert_eq!(latest_times, vec![3, 4, 5]); -} + let graph = Graph::new(); -#[test] -fn time_test() { - global_info_logger(); - let g = Graph::new(); + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; + let actual = (1..=3) + .map(|i| { + let v = graph.node(i).unwrap(); + ( + v.window(-1, 7).in_edges().iter().count(), + v.window(1, 7).out_edges().iter().count(), + v.window(0, 1).edges().iter().count(), + ) + }) + .collect::>(); - assert_eq!(g.latest_time(), None); - assert_eq!(g.earliest_time(), None); + assert_eq!(actual, expected); + } - g.add_node(5, 1, NO_PROPS, None, None) + #[test] + fn test_explode_layers_time() { + global_info_logger(); + let g = Graph::new(); + g.add_edge( + 1, + 1, + 2, + vec![("duration".to_string(), Prop::U32(5))], + Some("a"), + ) + .map_err(|err| error!("{:?}", err)) + .ok(); + g.add_edge( + 2, + 1, + 2, + vec![("duration".to_string(), Prop::U32(5))], + Some("a"), + ) + .map_err(|err| error!("{:?}", err)) + .ok(); + g.add_edge( + 3, + 1, + 2, + vec![("duration".to_string(), Prop::U32(5))], + Some("a"), + ) + .map_err(|err| error!("{:?}", err)) + .ok(); + g.add_edge( + 4, + 1, + 2, + vec![("duration".to_string(), Prop::U32(6))], + Some("b"), + ) .map_err(|err| error!("{:?}", err)) .ok(); + g.add_edge(5, 1, 2, NO_PROPS, Some("c")) + .map_err(|err| error!("{:?}", err)) + .ok(); - assert_eq!(g.latest_time().unwrap().t(), 5); - assert_eq!(g.earliest_time().unwrap().t(), 5); + assert_eq!(g.latest_time().unwrap().t(), 5); - let g = Graph::new(); + let earliest_times = g + .edge(1, 2) + .unwrap() + .explode_layers() + .earliest_time() + .map(|t| t.unwrap().t()) + .collect_vec(); - g.add_edge(10, 1, 2, NO_PROPS, None).unwrap(); - assert_eq!(g.latest_time().unwrap().t(), 10); - assert_eq!(g.earliest_time().unwrap().t(), 10); + assert_eq!(earliest_times, vec![1, 4, 5]); - g.add_node(5, 1, NO_PROPS, None, None) - .map_err(|err| error!("{:?}", err)) - .ok(); - assert_eq!(g.latest_time().unwrap().t(), 10); - assert_eq!(g.earliest_time().unwrap().t(), 5); + let latest_times = g + .edge(1, 2) + .unwrap() + .explode_layers() + .latest_time() + .map(|t| t.unwrap().t()) + .collect_vec(); - g.add_edge(20, 3, 4, NO_PROPS, None).unwrap(); - assert_eq!(g.latest_time().unwrap().t(), 20); - assert_eq!(g.earliest_time().unwrap().t(), 5); + assert_eq!(latest_times, vec![3, 4, 5]); + } - random_attachment(&g, 100, 10, None); - assert_eq!(g.latest_time().unwrap().t(), 126); - assert_eq!(g.earliest_time().unwrap().t(), 5); -} + #[test] + fn time_test() { + global_info_logger(); + let g = Graph::new(); -#[test] -fn test_metadata_props() { - let g = Graph::new(); - let n = g.add_node(1, 1, [("p1", 1)], None, None).unwrap(); - n.add_metadata([("m1", 1)]).unwrap(); - let n = g.add_node(1, 2, [("p2", 2)], None, None).unwrap(); - n.add_metadata([("m2", 2)]).unwrap(); - - let n1_meta = g - .node(1) - .unwrap() - .metadata() - .keys() - .map(|s| s.to_string()) - .collect::>(); - assert_eq!(n1_meta, vec!["m1", "m2"]); - let n1_props = g - .node(1) - .unwrap() - .properties() - .keys() - .map(|s| s.to_string()) - .collect::>(); - assert_eq!(n1_props, vec!["p1", "p2"]); -} + assert_eq!(g.latest_time(), None); + assert_eq!(g.earliest_time(), None); -#[test] -fn metadata() { - let g = Graph::new(); - g.add_edge(0, 11, 22, NO_PROPS, None).unwrap(); - g.add_edge( - 0, - 11, - 11, - vec![("temp".to_string(), Prop::Bool(true))], - None, - ) - .unwrap(); - g.add_edge(0, 22, 33, NO_PROPS, None).unwrap(); - g.add_edge(0, 33, 11, NO_PROPS, None).unwrap(); - g.add_node( - 0, - 11, - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - g.add_edge(0, 44, 55, NO_PROPS, None).unwrap(); - let v11 = g.node(11).unwrap(); - let v22 = g.node(22).unwrap(); - let v33 = g.node(33).unwrap(); - let v44 = g.node(44).unwrap(); - let v55 = g.node(55).unwrap(); - let edge1111 = g.edge(&v11, &v11).unwrap(); - let edge2233 = g.edge(&v22, &v33).unwrap(); - let edge3311 = g.edge(&v33, &v11).unwrap(); - - v11.add_metadata(vec![("a", Prop::U64(11)), ("b", Prop::I64(11))]) - .unwrap(); - v11.add_metadata(vec![("c", Prop::U32(11))]).unwrap(); + g.add_node(5, 1, NO_PROPS, None, None) + .map_err(|err| error!("{:?}", err)) + .ok(); - v44.add_metadata(vec![("e", Prop::U8(1))]).unwrap(); - v55.add_metadata(vec![("f", Prop::U16(1))]).unwrap(); - edge1111 - .add_metadata(vec![("d", Prop::U64(1111))], None) - .unwrap(); - edge3311 - .add_metadata(vec![("a", Prop::U64(3311))], None) - .unwrap(); + assert_eq!(g.latest_time().unwrap().t(), 5); + assert_eq!(g.earliest_time().unwrap().t(), 5); - // cannot add properties to non-existant layer - assert!(edge1111 - .add_metadata([("test", "test")], Some("test")) - .is_err()); - - // cannot change property type - assert!(v22.add_metadata(vec![("b", Prop::U64(22))]).is_err()); - - // TODO: Revisit this test after metadata handling is finalised. - // Refer to the `test_metadata_props` test for context. - // assert_eq!( - // v11.metadata().keys().collect::>(), - // vec!["a", "b", "c"] - // ); - // assert!(v22.metadata().keys().next().is_none()); - // assert!(v33.metadata().keys().next().is_none()); - // assert_eq!(v44.metadata().keys().collect::>(), vec!["e"]); - // assert_eq!(v55.metadata().keys().collect::>(), vec!["f"]); - - assert_eq!( - edge1111.metadata().keys().collect::>(), - vec!["d", "a"] // all edges get all ids anyhow - ); - assert_eq!( - edge3311.metadata().keys().collect::>(), - vec!["d", "a"] - ); - assert_eq!( - edge2233.metadata().keys().collect::>(), - vec!["d", "a"] - ); - - assert_eq!(v11.metadata().get("a"), Some(Prop::U64(11))); - assert_eq!(v11.metadata().get("b"), Some(Prop::I64(11))); - assert_eq!(v11.metadata().get("c"), Some(Prop::U32(11))); - assert_eq!(v22.metadata().get("b"), None); - assert_eq!(v44.metadata().get("e"), Some(Prop::U8(1))); - assert_eq!(v55.metadata().get("f"), Some(Prop::U16(1))); - assert_eq!(v22.metadata().get("a"), None); - assert_eq!(edge1111.metadata().get("d"), Some(Prop::U64(1111))); - assert_eq!(edge3311.metadata().get("a"), Some(Prop::U64(3311))); - assert_eq!(edge2233.metadata().get("a"), None); - - // cannot add properties to non-existant layer - assert!(edge1111 - .add_metadata([("test", "test")], Some("test")) - .is_err()); - g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); - // cannot add properties to layer without updates - assert!(edge1111 - .add_metadata([("test", "test")], Some("test")) - .is_err()); -} + let g = Graph::new(); -#[test] -fn temporal_node_rows_1_node() { - let graph = Graph::new(); + g.add_edge(10, 1, 2, NO_PROPS, None).unwrap(); + assert_eq!(g.latest_time().unwrap().t(), 10); + assert_eq!(g.earliest_time().unwrap().t(), 10); - graph - .add_node(0, 1, [("cool".to_string(), Prop::Bool(true))], None, None) - .unwrap(); + g.add_node(5, 1, NO_PROPS, None, None) + .map_err(|err| error!("{:?}", err)) + .ok(); + assert_eq!(g.latest_time().unwrap().t(), 10); + assert_eq!(g.earliest_time().unwrap().t(), 5); - test_storage!(&graph, |graph| { - let actual = graph - .node(1) - .unwrap() - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); + g.add_edge(20, 3, 4, NO_PROPS, None).unwrap(); + assert_eq!(g.latest_time().unwrap().t(), 20); + assert_eq!(g.earliest_time().unwrap().t(), 5); - assert_eq!(actual, vec![(0.into(), vec![Prop::Bool(true)])]); - }); + random_attachment(&g, 100, 10, None); + assert_eq!(g.latest_time().unwrap().t(), 126); + assert_eq!(g.earliest_time().unwrap().t(), 5); + } - graph - .add_node(0, 1, [("coolio".to_string(), Prop::U64(9))], None, None) - .unwrap(); + #[test] + fn test_metadata_props() { + let g = Graph::new(); + let n = g.add_node(1, 1, [("p1", 1)], None, None).unwrap(); + n.add_metadata([("m1", 1)]).unwrap(); + let n = g.add_node(1, 2, [("p2", 2)], None, None).unwrap(); + n.add_metadata([("m2", 2)]).unwrap(); - test_storage!(&graph, |graph| { - let actual = graph + let n1_meta = g .node(1) .unwrap() - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .metadata() + .keys() + .map(|s| s.to_string()) .collect::>(); + assert_eq!(n1_meta, vec!["m1", "m2"]); + let n1_props = g + .node(1) + .unwrap() + .properties() + .keys() + .map(|s| s.to_string()) + .collect::>(); + assert_eq!(n1_props, vec!["p1", "p2"]); + } - assert_eq!( - actual, - vec![ - (EventTime::new(0, 0), vec![Prop::Bool(true)]), - (EventTime::new(0, 1), vec![Prop::U64(9)]) - ] - ); - }); - - graph - .add_node( - 1, - 1, - [ - ("cool".to_string(), Prop::Bool(false)), - ("coolio".to_string(), Prop::U64(19)), - ], + #[test] + fn metadata() { + let g = Graph::new(); + g.add_edge(0, 11, 22, NO_PROPS, None).unwrap(); + g.add_edge( + 0, + 11, + 11, + vec![("temp".to_string(), Prop::Bool(true))], + None, + ) + .unwrap(); + g.add_edge(0, 22, 33, NO_PROPS, None).unwrap(); + g.add_edge(0, 33, 11, NO_PROPS, None).unwrap(); + g.add_node( + 0, + 11, + vec![("temp".to_string(), Prop::Bool(true))], None, None, ) .unwrap(); + g.add_edge(0, 44, 55, NO_PROPS, None).unwrap(); + let v11 = g.node(11).unwrap(); + let v22 = g.node(22).unwrap(); + let v33 = g.node(33).unwrap(); + let v44 = g.node(44).unwrap(); + let v55 = g.node(55).unwrap(); + let edge1111 = g.edge(&v11, &v11).unwrap(); + let edge2233 = g.edge(&v22, &v33).unwrap(); + let edge3311 = g.edge(&v33, &v11).unwrap(); + + v11.add_metadata(vec![("a", Prop::U64(11)), ("b", Prop::I64(11))]) + .unwrap(); + v11.add_metadata(vec![("c", Prop::U32(11))]).unwrap(); - test_storage!(&graph, |graph| { - let actual = graph - .node(1) - .unwrap() - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); - - let expected = vec![ - (EventTime::new(0, 0), vec![Prop::Bool(true)]), - (EventTime::new(0, 1), vec![Prop::U64(9)]), - (EventTime::new(1, 2), vec![Prop::Bool(false), Prop::U64(19)]), - ]; - assert_eq!(actual, expected); - }); + v44.add_metadata(vec![("e", Prop::U8(1))]).unwrap(); + v55.add_metadata(vec![("f", Prop::U16(1))]).unwrap(); + edge1111 + .add_metadata(vec![("d", Prop::U64(1111))], None) + .unwrap(); + edge3311 + .add_metadata(vec![("a", Prop::U64(3311))], None) + .unwrap(); - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - // edge additions should not show up in prop rows - graph.add_edge(3, 1, 1, NO_PROPS, None).unwrap(); + // cannot add properties to non-existant layer + assert!(edge1111 + .add_metadata([("test", "test")], Some("test")) + .is_err()); + + // cannot change property type + assert!(v22.add_metadata(vec![("b", Prop::U64(22))]).is_err()); + + // TODO: Revisit this test after metadata handling is finalised. + // Refer to the `test_metadata_props` test for context. + // assert_eq!( + // v11.metadata().keys().collect::>(), + // vec!["a", "b", "c"] + // ); + // assert!(v22.metadata().keys().next().is_none()); + // assert!(v33.metadata().keys().next().is_none()); + // assert_eq!(v44.metadata().keys().collect::>(), vec!["e"]); + // assert_eq!(v55.metadata().keys().collect::>(), vec!["f"]); - test_storage!(&graph, |graph| { - let actual = graph - .node(1) - .unwrap() - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); + assert_eq!( + edge1111.metadata().keys().collect::>(), + vec!["d", "a"] // all edges get all ids anyhow + ); + assert_eq!( + edge3311.metadata().keys().collect::>(), + vec!["d", "a"] + ); + assert_eq!( + edge2233.metadata().keys().collect::>(), + vec!["d", "a"] + ); - let expected = vec![ - (EventTime::new(0, 0), vec![Prop::Bool(true)]), - (EventTime::new(0, 1), vec![Prop::U64(9)]), - (EventTime::new(1, 2), vec![Prop::Bool(false), Prop::U64(19)]), - (EventTime::new(2, 3), vec![]), - ]; + assert_eq!(v11.metadata().get("a"), Some(Prop::U64(11))); + assert_eq!(v11.metadata().get("b"), Some(Prop::I64(11))); + assert_eq!(v11.metadata().get("c"), Some(Prop::U32(11))); + assert_eq!(v22.metadata().get("b"), None); + assert_eq!(v44.metadata().get("e"), Some(Prop::U8(1))); + assert_eq!(v55.metadata().get("f"), Some(Prop::U16(1))); + assert_eq!(v22.metadata().get("a"), None); + assert_eq!(edge1111.metadata().get("d"), Some(Prop::U64(1111))); + assert_eq!(edge3311.metadata().get("a"), Some(Prop::U64(3311))); + assert_eq!(edge2233.metadata().get("a"), None); + + // cannot add properties to non-existant layer + assert!(edge1111 + .add_metadata([("test", "test")], Some("test")) + .is_err()); + g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); + // cannot add properties to layer without updates + assert!(edge1111 + .add_metadata([("test", "test")], Some("test")) + .is_err()); + } - assert_eq!(actual, expected); - }); -} + #[test] + fn temporal_node_rows_1_node() { + let graph = Graph::new(); -#[test] -fn temporal_node_rows_nodes() { - let graph = Graph::new(); - let mut nodes = Vec::new(); - nodes.push( graph - .add_node(0, 1, [("cool".to_string(), Prop::U64(1))], None, None) - .unwrap() - .node, - ); - nodes.push( + .add_node(0, 1, [("cool".to_string(), Prop::Bool(true))], None, None) + .unwrap(); + + test_storage!(&graph, |graph| { + let actual = graph + .node(1) + .unwrap() + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); + + assert_eq!(actual, vec![(0.into(), vec![Prop::Bool(true)])]); + }); + graph - .add_node(1, 2, [("cool".to_string(), Prop::U64(2))], None, None) - .unwrap() - .node, - ); - nodes.push( + .add_node(0, 1, [("coolio".to_string(), Prop::U64(9))], None, None) + .unwrap(); + + test_storage!(&graph, |graph| { + let actual = graph + .node(1) + .unwrap() + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); + + assert_eq!( + actual, + vec![ + (EventTime::new(0, 0), vec![Prop::Bool(true)]), + (EventTime::new(0, 1), vec![Prop::U64(9)]) + ] + ); + }); + graph - .add_node(2, 3, [("cool".to_string(), Prop::U64(3))], None, None) - .unwrap() - .node, - ); + .add_node( + 1, + 1, + [ + ("cool".to_string(), Prop::Bool(false)), + ("coolio".to_string(), Prop::U64(19)), + ], + None, + None, + ) + .unwrap(); - let prop_ids: Arc<[usize]> = graph.node_meta().temporal_prop_mapper().ids().collect(); - for (id, n) in nodes.into_iter().enumerate() { - let actual = graph - .core_graph() - .nodes() - .node(n) - .temp_prop_rows(prop_ids.clone()) - .map(|(t, _, row)| (t, row.into_iter().map(|(_, p)| p).collect::>())) - .collect::>(); + test_storage!(&graph, |graph| { + let actual = graph + .node(1) + .unwrap() + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); - let expected = vec![( - EventTime::new(id as i64, id), - vec![Prop::U64((id as u64) + 1)], - )]; - assert_eq!(actual, expected); + let expected = vec![ + (EventTime::new(0, 0), vec![Prop::Bool(true)]), + (EventTime::new(0, 1), vec![Prop::U64(9)]), + (EventTime::new(1, 2), vec![Prop::Bool(false), Prop::U64(19)]), + ]; + assert_eq!(actual, expected); + }); + + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + // edge additions should not show up in prop rows + graph.add_edge(3, 1, 1, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let actual = graph + .node(1) + .unwrap() + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); + + let expected = vec![ + (EventTime::new(0, 0), vec![Prop::Bool(true)]), + (EventTime::new(0, 1), vec![Prop::U64(9)]), + (EventTime::new(1, 2), vec![Prop::Bool(false), Prop::U64(19)]), + (EventTime::new(2, 3), vec![]), + ]; + + assert_eq!(actual, expected); + }); } -} -#[test] -fn temporal_node_rows_window() { - let graph = Graph::new(); - graph - .add_node(0, 1, [("cool".to_string(), Prop::U64(1))], None, None) - .unwrap(); - graph - .add_node(1, 1, [("cool".to_string(), Prop::U64(2))], None, None) - .unwrap(); - graph - .add_node(2, 1, [("cool".to_string(), Prop::U64(3))], None, None) - .unwrap(); + #[test] + fn temporal_node_rows_nodes() { + let graph = Graph::new(); + let mut nodes = Vec::new(); + nodes.push( + graph + .add_node(0, 1, [("cool".to_string(), Prop::U64(1))], None, None) + .unwrap() + .node, + ); + nodes.push( + graph + .add_node(1, 2, [("cool".to_string(), Prop::U64(2))], None, None) + .unwrap() + .node, + ); + nodes.push( + graph + .add_node(2, 3, [("cool".to_string(), Prop::U64(3))], None, None) + .unwrap() + .node, + ); - test_storage!(&graph, |graph| { let prop_ids: Arc<[usize]> = graph.node_meta().temporal_prop_mapper().ids().collect(); - let get_rows = |vid: VID, range: Range| { - graph + for (id, n) in nodes.into_iter().enumerate() { + let actual = graph .core_graph() .nodes() - .node(vid) - .temp_prop_rows_range(Some(range), prop_ids.clone()) + .node(n) + .temp_prop_rows(prop_ids.clone()) .map(|(t, _, row)| (t, row.into_iter().map(|(_, p)| p).collect::>())) - .collect::>() - }; - let actual = get_rows(VID(0), EventTime::new(2, 0)..EventTime::new(3, 0)); + .collect::>(); - let expected = vec![(EventTime::new(2, 2), vec![Prop::U64(3)])]; + let expected = vec![( + EventTime::new(id as i64, id), + vec![Prop::U64((id as u64) + 1)], + )]; + assert_eq!(actual, expected); + } + } - assert_eq!(actual, expected); + #[test] + fn temporal_node_rows_window() { + let graph = Graph::new(); + graph + .add_node(0, 1, [("cool".to_string(), Prop::U64(1))], None, None) + .unwrap(); + graph + .add_node(1, 1, [("cool".to_string(), Prop::U64(2))], None, None) + .unwrap(); + graph + .add_node(2, 1, [("cool".to_string(), Prop::U64(3))], None, None) + .unwrap(); - let actual = get_rows(VID(0), EventTime::new(0, 0)..EventTime::new(3, 0)); - let expected = vec![ - (EventTime::new(0, 0), vec![Prop::U64(1)]), - (EventTime::new(1, 1), vec![Prop::U64(2)]), - (EventTime::new(2, 2), vec![Prop::U64(3)]), - ]; + test_storage!(&graph, |graph| { + let prop_ids: Arc<[usize]> = graph.node_meta().temporal_prop_mapper().ids().collect(); + let get_rows = |vid: VID, range: Range| { + graph + .core_graph() + .nodes() + .node(vid) + .temp_prop_rows_range(Some(range), prop_ids.clone()) + .map(|(t, _, row)| (t, row.into_iter().map(|(_, p)| p).collect::>())) + .collect::>() + }; + let actual = get_rows(VID(0), EventTime::new(2, 0)..EventTime::new(3, 0)); + + let expected = vec![(EventTime::new(2, 2), vec![Prop::U64(3)])]; + + assert_eq!(actual, expected); + + let actual = get_rows(VID(0), EventTime::new(0, 0)..EventTime::new(3, 0)); + let expected = vec![ + (EventTime::new(0, 0), vec![Prop::U64(1)]), + (EventTime::new(1, 1), vec![Prop::U64(2)]), + (EventTime::new(2, 2), vec![Prop::U64(3)]), + ]; + + assert_eq!(actual, expected); + }); + } - assert_eq!(actual, expected); - }); -} + #[test] + fn temporal_props_node() { + let graph = Graph::new(); -#[test] -fn temporal_props_node() { - let graph = Graph::new(); + graph + .add_node(0, 1, [("cool".to_string(), Prop::Bool(true))], None, None) + .unwrap(); - graph - .add_node(0, 1, [("cool".to_string(), Prop::Bool(true))], None, None) - .unwrap(); + let v = graph.node(1).unwrap(); - let v = graph.node(1).unwrap(); + let actual = v.properties().get("cool"); + assert_eq!(actual, Some(Prop::Bool(true))); - let actual = v.properties().get("cool"); - assert_eq!(actual, Some(Prop::Bool(true))); + // we flip cool from true to false after t 3 + graph + .add_node(3, 1, [("cool".to_string(), Prop::Bool(false))], None, None) + .unwrap(); - // we flip cool from true to false after t 3 - graph - .add_node(3, 1, [("cool".to_string(), Prop::Bool(false))], None, None) - .unwrap(); + test_storage!(&graph, |graph| { + let wg = graph.window(3, 15); + let v = wg.node(1).unwrap(); - test_storage!(&graph, |graph| { - let wg = graph.window(3, 15); - let v = wg.node(1).unwrap(); + let actual = v.properties().get("cool"); + assert_eq!(actual, Some(Prop::Bool(false))); - let actual = v.properties().get("cool"); - assert_eq!(actual, Some(Prop::Bool(false))); + let hist: Vec<_> = v + .properties() + .temporal() + .get("cool") + .unwrap() + .iter() + .map(|(x, y)| (x.t(), y)) + .collect(); + assert_eq!(hist, vec![(3, Prop::Bool(false))]); - let hist: Vec<_> = v - .properties() - .temporal() - .get("cool") - .unwrap() - .iter() - .map(|(x, y)| (x.t(), y)) - .collect(); - assert_eq!(hist, vec![(3, Prop::Bool(false))]); + let v = graph.node(1).unwrap(); - let v = graph.node(1).unwrap(); + let hist: Vec<_> = v + .properties() + .temporal() + .get("cool") + .unwrap() + .iter() + .map(|(x, y)| (x.t(), y)) + .collect(); + assert_eq!(hist, vec![(0, Prop::Bool(true)), (3, Prop::Bool(false))]); + }); + } - let hist: Vec<_> = v - .properties() - .temporal() - .get("cool") - .unwrap() - .iter() - .map(|(x, y)| (x.t(), y)) - .collect(); - assert_eq!(hist, vec![(0, Prop::Bool(true)), (3, Prop::Bool(false))]); - }); -} + #[test] + fn temporal_props_edge() { + let graph = Graph::new(); -#[test] -fn temporal_props_edge() { - let graph = Graph::new(); + graph + .add_edge(1, 0, 1, vec![("distance".to_string(), Prop::U32(5))], None) + .expect("add edge"); - graph - .add_edge(1, 0, 1, vec![("distance".to_string(), Prop::U32(5))], None) - .expect("add edge"); + test_storage!(&graph, |graph| { + let e = graph.edge(0, 1).unwrap(); - test_storage!(&graph, |graph| { - let e = graph.edge(0, 1).unwrap(); + let prop = e.properties().get("distance").unwrap(); + assert_eq!(prop, Prop::U32(5)); + }); + } - let prop = e.properties().get("distance").unwrap(); - assert_eq!(prop, Prop::U32(5)); - }); -} + #[test] + fn graph_neighbours_window() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; -#[test] -fn graph_neighbours_window() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; + let graph = Graph::new(); - let graph = Graph::new(); + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let expected = vec![ + (vec![1, 2], vec![1, 2, 3], vec![1]), + (vec![1], vec![], vec![]), + (vec![1], vec![], vec![]), + ]; + let actual = (1..=3) + .map(|i| { + let v = graph.node(i).unwrap(); + ( + v.window(-1, 7) + .in_neighbours() + .id() + .filter_map(|id| id.as_u64()) + .collect::>(), + v.window(1, 7) + .out_neighbours() + .id() + .filter_map(|id| id.as_u64()) + .collect::>(), + v.window(0, 1) + .neighbours() + .id() + .filter_map(|id| id.as_u64()) + .collect::>(), + ) + }) + .collect::>(); - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + assert_eq!(actual, expected); + }); } - test_storage!(&graph, |graph| { - let expected = vec![ - (vec![1, 2], vec![1, 2, 3], vec![1]), - (vec![1], vec![], vec![]), - (vec![1], vec![], vec![]), - ]; - let actual = (1..=3) - .map(|i| { - let v = graph.node(i).unwrap(); - ( - v.window(-1, 7) - .in_neighbours() - .id() - .filter_map(|id| id.as_u64()) - .collect::>(), - v.window(1, 7) - .out_neighbours() - .id() - .filter_map(|id| id.as_u64()) - .collect::>(), - v.window(0, 1) - .neighbours() - .id() - .filter_map(|id| id.as_u64()) - .collect::>(), - ) - }) - .collect::>(); - - assert_eq!(actual, expected); - }); -} - -#[test] -fn test_time_range_on_empty_graph() { - let graph = Graph::new(); - test_storage!(&graph, |graph| { - let rolling = graph.rolling(1, None).unwrap().collect_vec(); - assert!(rolling.is_empty()); + #[test] + fn test_time_range_on_empty_graph() { + let graph = Graph::new(); - let expanding = graph.expanding(1).unwrap().collect_vec(); - assert!(expanding.is_empty()); - }); -} + test_storage!(&graph, |graph| { + let rolling = graph.rolling(1, None).unwrap().collect_vec(); + assert!(rolling.is_empty()); -#[test] -fn test_add_node_with_nums() { - let graph = Graph::new(); + let expanding = graph.expanding(1).unwrap().collect_vec(); + assert!(expanding.is_empty()); + }); + } - graph.add_node(1, 831, NO_PROPS, None, None).unwrap(); - test_storage!(&graph, |graph| { - assert!(graph.has_node(831)); + #[test] + fn test_add_node_with_nums() { + let graph = Graph::new(); - assert_eq!(graph.count_nodes(), 1); - }); -} + graph.add_node(1, 831, NO_PROPS, None, None).unwrap(); + test_storage!(&graph, |graph| { + assert!(graph.has_node(831)); -#[test] -fn test_add_node_with_strings() { - let graph = Graph::new(); + assert_eq!(graph.count_nodes(), 1); + }); + } - graph.add_node(0, "haaroon", NO_PROPS, None, None).unwrap(); - graph.add_node(1, "hamza", NO_PROPS, None, None).unwrap(); - test_storage!(&graph, |graph| { - assert!(graph.has_node("haaroon")); - assert!(graph.has_node("hamza")); + #[test] + fn test_add_node_with_strings() { + let graph = Graph::new(); - assert_eq!(graph.count_nodes(), 2); - }); -} + graph.add_node(0, "haaroon", NO_PROPS, None, None).unwrap(); + graph.add_node(1, "hamza", NO_PROPS, None, None).unwrap(); + test_storage!(&graph, |graph| { + assert!(graph.has_node("haaroon")); + assert!(graph.has_node("hamza")); -#[test] -fn layers() -> Result<(), GraphError> { - let graph = Graph::new(); - graph.add_edge(0, 11, 22, NO_PROPS, None)?; - graph.add_edge(0, 11, 33, NO_PROPS, None)?; - graph.add_edge(0, 33, 11, NO_PROPS, None)?; - graph.add_edge(0, 11, 22, NO_PROPS, Some("layer1"))?; - graph.add_edge(0, 11, 33, NO_PROPS, Some("layer2"))?; - graph.add_edge(0, 11, 44, NO_PROPS, Some("layer2"))?; - - assert!(graph.has_edge(11, 22)); - assert!(graph.default_layer().has_edge(11, 22)); - assert!(!graph.default_layer().has_edge(11, 44)); - assert!(!graph.layers("layer2")?.has_edge(11, 22)); - assert!(graph.layers("layer2")?.has_edge(11, 44)); - - assert!(graph.edge(11, 22).is_some()); - assert!(graph.layers(Layer::Default)?.edge(11, 44).is_none()); - assert!(graph.layers("layer2")?.edge(11, 22).is_none()); - assert!(graph.layers("layer2")?.edge(11, 44).is_some()); - - assert!(graph.exclude_layers("layer2")?.edge(11, 44).is_none()); - assert!(graph.exclude_layers("layer2")?.edge(11, 33).is_some()); - assert!(graph.exclude_layers("layer2")?.edge(11, 22).is_some()); - - let dft_layer = graph.default_layer(); - let layer1 = graph.layers("layer1")?; - let layer2 = graph.layers("layer2")?; - assert!(graph.layers("missing layer").is_err()); - - assert_eq!(graph.count_nodes(), 4); - assert_eq!(graph.count_edges(), 4); - assert_eq!(dft_layer.count_edges(), 3); - assert_eq!(layer1.count_edges(), 1); - assert_eq!(layer2.count_edges(), 2); - - let node = graph.node(11).unwrap(); - let node_dft = dft_layer.node(11).unwrap(); - let node1 = layer1.node(11).unwrap(); - let node2 = layer2.node(11).unwrap(); - - assert_eq!(node.degree(), 3); - assert_eq!(node_dft.degree(), 2); - assert_eq!(node1.degree(), 1); - assert_eq!(node2.degree(), 2); - - assert_eq!(node.out_degree(), 3); - assert_eq!(node_dft.out_degree(), 2); - assert_eq!(node1.out_degree(), 1); - assert_eq!(node2.out_degree(), 2); - - assert_eq!(node.in_degree(), 1); - assert_eq!(node_dft.in_degree(), 1); - assert_eq!(node1.in_degree(), 0); - assert_eq!(node2.in_degree(), 0); - - fn to_tuples<'graph, G: GraphViewOps<'graph>>(edges: Edges<'graph, G>) -> Vec<(u64, u64)> { - edges - .id() - .filter_map(|(s, d)| s.to_u64().zip(d.to_u64())) - .sorted() - .collect_vec() + assert_eq!(graph.count_nodes(), 2); + }); } - assert_eq!( - to_tuples(node.edges()), - vec![(11, 22), (11, 33), (11, 44), (33, 11)] - ); - assert_eq!( - to_tuples(node_dft.edges()), - vec![(11, 22), (11, 33), (33, 11)] - ); - assert_eq!(to_tuples(node1.edges()), vec![(11, 22)]); - assert_eq!(to_tuples(node2.edges()), vec![(11, 33), (11, 44)]); - - assert_eq!(to_tuples(node.in_edges()), vec![(33, 11)]); - assert_eq!(to_tuples(node_dft.in_edges()), vec![(33, 11)]); - assert_eq!(to_tuples(node1.in_edges()), vec![]); - assert_eq!(to_tuples(node2.in_edges()), vec![]); - - assert_eq!( - to_tuples(node.out_edges()), - vec![(11, 22), (11, 33), (11, 44)] - ); - assert_eq!(to_tuples(node_dft.out_edges()), vec![(11, 22), (11, 33)]); - assert_eq!(to_tuples(node1.out_edges()), vec![(11, 22)]); - assert_eq!(to_tuples(node2.out_edges()), vec![(11, 33), (11, 44)]); - - fn to_ids<'graph, G: GraphViewOps<'graph>>(neighbours: PathFromNode<'graph, G>) -> Vec { - neighbours - .iter() - .filter_map(|n| n.id().as_u64()) - .sorted() - .collect_vec() - } + #[test] + fn layers() -> Result<(), GraphError> { + let graph = Graph::new(); + graph.add_edge(0, 11, 22, NO_PROPS, None)?; + graph.add_edge(0, 11, 33, NO_PROPS, None)?; + graph.add_edge(0, 33, 11, NO_PROPS, None)?; + graph.add_edge(0, 11, 22, NO_PROPS, Some("layer1"))?; + graph.add_edge(0, 11, 33, NO_PROPS, Some("layer2"))?; + graph.add_edge(0, 11, 44, NO_PROPS, Some("layer2"))?; + + assert!(graph.has_edge(11, 22)); + assert!(graph.default_layer().has_edge(11, 22)); + assert!(!graph.default_layer().has_edge(11, 44)); + assert!(!graph.layers("layer2")?.has_edge(11, 22)); + assert!(graph.layers("layer2")?.has_edge(11, 44)); + + assert!(graph.edge(11, 22).is_some()); + assert!(graph.layers(Layer::Default)?.edge(11, 44).is_none()); + assert!(graph.layers("layer2")?.edge(11, 22).is_none()); + assert!(graph.layers("layer2")?.edge(11, 44).is_some()); + + assert!(graph.exclude_layers("layer2")?.edge(11, 44).is_none()); + assert!(graph.exclude_layers("layer2")?.edge(11, 33).is_some()); + assert!(graph.exclude_layers("layer2")?.edge(11, 22).is_some()); + + let dft_layer = graph.default_layer(); + let layer1 = graph.layers("layer1")?; + let layer2 = graph.layers("layer2")?; + assert!(graph.layers("missing layer").is_err()); + + assert_eq!(graph.count_nodes(), 4); + assert_eq!(graph.count_edges(), 4); + assert_eq!(dft_layer.count_edges(), 3); + assert_eq!(layer1.count_edges(), 1); + assert_eq!(layer2.count_edges(), 2); + + let node = graph.node(11).unwrap(); + let node_dft = dft_layer.node(11).unwrap(); + let node1 = layer1.node(11).unwrap(); + let node2 = layer2.node(11).unwrap(); + + assert_eq!(node.degree(), 3); + assert_eq!(node_dft.degree(), 2); + assert_eq!(node1.degree(), 1); + assert_eq!(node2.degree(), 2); + + assert_eq!(node.out_degree(), 3); + assert_eq!(node_dft.out_degree(), 2); + assert_eq!(node1.out_degree(), 1); + assert_eq!(node2.out_degree(), 2); + + assert_eq!(node.in_degree(), 1); + assert_eq!(node_dft.in_degree(), 1); + assert_eq!(node1.in_degree(), 0); + assert_eq!(node2.in_degree(), 0); + + fn to_tuples<'graph, G: GraphViewOps<'graph>>(edges: Edges<'graph, G>) -> Vec<(u64, u64)> { + edges + .id() + .filter_map(|(s, d)| s.to_u64().zip(d.to_u64())) + .sorted() + .collect_vec() + } - assert_eq!(to_ids(node.neighbours()), vec![22, 33, 44]); - assert_eq!(to_ids(node_dft.neighbours()), vec![22, 33]); - assert_eq!(to_ids(node1.neighbours()), vec![22]); - assert_eq!(to_ids(node2.neighbours()), vec![33, 44]); + assert_eq!( + to_tuples(node.edges()), + vec![(11, 22), (11, 33), (11, 44), (33, 11)] + ); + assert_eq!( + to_tuples(node_dft.edges()), + vec![(11, 22), (11, 33), (33, 11)] + ); + assert_eq!(to_tuples(node1.edges()), vec![(11, 22)]); + assert_eq!(to_tuples(node2.edges()), vec![(11, 33), (11, 44)]); - assert_eq!(to_ids(node.out_neighbours()), vec![22, 33, 44]); - assert_eq!(to_ids(node_dft.out_neighbours()), vec![22, 33]); - assert_eq!(to_ids(node1.out_neighbours()), vec![22]); - assert_eq!(to_ids(node2.out_neighbours()), vec![33, 44]); + assert_eq!(to_tuples(node.in_edges()), vec![(33, 11)]); + assert_eq!(to_tuples(node_dft.in_edges()), vec![(33, 11)]); + assert_eq!(to_tuples(node1.in_edges()), vec![]); + assert_eq!(to_tuples(node2.in_edges()), vec![]); - assert_eq!(to_ids(node.in_neighbours()), vec![33]); - assert_eq!(to_ids(node_dft.in_neighbours()), vec![33]); - assert!(to_ids(node1.in_neighbours()).is_empty()); - assert!(to_ids(node2.in_neighbours()).is_empty()); - Ok(()) -} + assert_eq!( + to_tuples(node.out_edges()), + vec![(11, 22), (11, 33), (11, 44)] + ); + assert_eq!(to_tuples(node_dft.out_edges()), vec![(11, 22), (11, 33)]); + assert_eq!(to_tuples(node1.out_edges()), vec![(11, 22)]); + assert_eq!(to_tuples(node2.out_edges()), vec![(11, 33), (11, 44)]); + + fn to_ids<'graph, G: GraphViewOps<'graph>>( + neighbours: PathFromNode<'graph, G>, + ) -> Vec { + neighbours + .iter() + .filter_map(|n| n.id().as_u64()) + .sorted() + .collect_vec() + } -#[test] -fn test_props() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("weight", Prop::I64(1))], None) - .unwrap(); - g.add_edge(1, 1, 2, [("weight", Prop::I64(2))], None) - .unwrap(); - g.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); - - let exploded = g.edge(1, 2).unwrap().explode(); - let res = exploded.properties().map(|p| p.as_vec()).collect_vec(); - assert_eq!( - res, - vec![ - vec![("weight".into(), Prop::I64(1))], - vec![("weight".into(), Prop::I64(2))], - vec![] - ] - ); -} + assert_eq!(to_ids(node.neighbours()), vec![22, 33, 44]); + assert_eq!(to_ids(node_dft.neighbours()), vec![22, 33]); + assert_eq!(to_ids(node1.neighbours()), vec![22]); + assert_eq!(to_ids(node2.neighbours()), vec![33, 44]); + + assert_eq!(to_ids(node.out_neighbours()), vec![22, 33, 44]); + assert_eq!(to_ids(node_dft.out_neighbours()), vec![22, 33]); + assert_eq!(to_ids(node1.out_neighbours()), vec![22]); + assert_eq!(to_ids(node2.out_neighbours()), vec![33, 44]); + + assert_eq!(to_ids(node.in_neighbours()), vec![33]); + assert_eq!(to_ids(node_dft.in_neighbours()), vec![33]); + assert!(to_ids(node1.in_neighbours()).is_empty()); + assert!(to_ids(node2.in_neighbours()).is_empty()); + Ok(()) + } -#[test] -fn test_exploded_edge() { - let graph = Graph::new(); - graph - .add_edge(0, 1, 2, [("weight", Prop::I64(1))], None) - .unwrap(); - graph - .add_edge(1, 1, 2, [("weight", Prop::I64(2))], None) - .unwrap(); - graph - .add_edge(2, 1, 2, [("weight", Prop::I64(3))], None) - .unwrap(); - test_storage!(&graph, |graph| { - let exploded = graph.edge(1, 2).unwrap().explode(); + #[test] + fn test_props() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("weight", Prop::I64(1))], None) + .unwrap(); + g.add_edge(1, 1, 2, [("weight", Prop::I64(2))], None) + .unwrap(); + g.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); + let exploded = g.edge(1, 2).unwrap().explode(); let res = exploded.properties().map(|p| p.as_vec()).collect_vec(); + assert_eq!( + res, + vec![ + vec![("weight".into(), Prop::I64(1))], + vec![("weight".into(), Prop::I64(2))], + vec![] + ] + ); + } - let mut expected = Vec::new(); - for i in 1..4 { - expected.push(vec![("weight".into(), Prop::I64(i))]); - } + #[test] + fn test_exploded_edge() { + let graph = Graph::new(); + graph + .add_edge(0, 1, 2, [("weight", Prop::I64(1))], None) + .unwrap(); + graph + .add_edge(1, 1, 2, [("weight", Prop::I64(2))], None) + .unwrap(); + graph + .add_edge(2, 1, 2, [("weight", Prop::I64(3))], None) + .unwrap(); + test_storage!(&graph, |graph| { + let exploded = graph.edge(1, 2).unwrap().explode(); - assert_eq!(res, expected); + let res = exploded.properties().map(|p| p.as_vec()).collect_vec(); - let e = graph - .node(1) - .unwrap() - .edges() - .explode() - .properties() - .map(|p| p.as_vec()) - .collect_vec(); - assert_eq!(e, expected); - }); -} + let mut expected = Vec::new(); + for i in 1..4 { + expected.push(vec![("weight".into(), Prop::I64(i))]); + } -#[test] -fn test_edge_earliest_latest() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); + assert_eq!(res, expected); - test_storage!(&graph, |graph| { - let mut res = graph.edge(1, 2).unwrap().earliest_time().unwrap(); - assert_eq!(res, 0); + let e = graph + .node(1) + .unwrap() + .edges() + .explode() + .properties() + .map(|p| p.as_vec()) + .collect_vec(); + assert_eq!(e, expected); + }); + } - res = graph.edge(1, 2).unwrap().latest_time().unwrap(); - assert_eq!(res, 2); + #[test] + fn test_edge_earliest_latest() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); - res = graph.at(1).edge(1, 2).unwrap().earliest_time().unwrap(); - assert_eq!(res, 1); + test_storage!(&graph, |graph| { + let mut res = graph.edge(1, 2).unwrap().earliest_time().unwrap(); + assert_eq!(res, 0); - res = graph.before(1).edge(1, 2).unwrap().earliest_time().unwrap(); - assert_eq!(res, 0); + res = graph.edge(1, 2).unwrap().latest_time().unwrap(); + assert_eq!(res, 2); - res = graph.after(1).edge(1, 2).unwrap().earliest_time().unwrap(); - assert_eq!(res, 2); + res = graph.at(1).edge(1, 2).unwrap().earliest_time().unwrap(); + assert_eq!(res, 1); - res = graph.at(1).edge(1, 2).unwrap().latest_time().unwrap(); - assert_eq!(res, 1); + res = graph.before(1).edge(1, 2).unwrap().earliest_time().unwrap(); + assert_eq!(res, 0); - res = graph.before(1).edge(1, 2).unwrap().latest_time().unwrap(); - assert_eq!(res, 0); + res = graph.after(1).edge(1, 2).unwrap().earliest_time().unwrap(); + assert_eq!(res, 2); - res = graph.after(1).edge(1, 2).unwrap().latest_time().unwrap(); - assert_eq!(res, 2); + res = graph.at(1).edge(1, 2).unwrap().latest_time().unwrap(); + assert_eq!(res, 1); - let res_list: Vec = graph - .node(1) - .unwrap() - .edges() - .earliest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![0, 0]); + res = graph.before(1).edge(1, 2).unwrap().latest_time().unwrap(); + assert_eq!(res, 0); - let res_list: Vec = graph - .node(1) - .unwrap() - .edges() - .latest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![2, 2]); + res = graph.after(1).edge(1, 2).unwrap().latest_time().unwrap(); + assert_eq!(res, 2); - let res_list: Vec = graph - .node(1) - .unwrap() - .at(1) - .edges() - .earliest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![1, 1]); + let res_list: Vec = graph + .node(1) + .unwrap() + .edges() + .earliest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![0, 0]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .edges() + .latest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![2, 2]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .at(1) + .edges() + .earliest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![1, 1]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .before(1) + .edges() + .earliest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![0, 0]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .after(1) + .edges() + .earliest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![2, 2]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .at(1) + .edges() + .latest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![1, 1]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .before(1) + .edges() + .latest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![0, 0]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .after(1) + .edges() + .latest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![2, 2]); + }); + } - let res_list: Vec = graph - .node(1) - .unwrap() - .before(1) - .edges() - .earliest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![0, 0]); + #[test] + fn node_properties() -> Result<(), GraphError> { + let g = Graph::new(); - let res_list: Vec = graph - .node(1) - .unwrap() - .after(1) - .edges() - .earliest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![2, 2]); + g.add_node( + 0, + 1, + [("t", Prop::str("wallet")), ("cost", Prop::F64(99.5))], + Some("a"), + None, + )?; - let res_list: Vec = graph - .node(1) - .unwrap() - .at(1) - .edges() - .latest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![1, 1]); + let n1 = g.node(1).unwrap(); + g.add_node(1, 2, [("t", Prop::str("person"))], None, None)?; + g.add_node( + 6, + 3, + [ + ("list_prop", vec![1.1, 2.2, 3.3].into_prop_list()), + ("cost_b", Prop::F64(76.0)), + ], + Some("b"), + None, + )?; - let res_list: Vec = graph - .node(1) - .unwrap() - .before(1) - .edges() - .latest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![0, 0]); + g.add_node( + 7, + 4, + [ + ("str_prop", Prop::str("hello")), + ("bool_prop", Prop::Bool(true)), + ], + Some("b"), + None, + )?; - let res_list: Vec = graph - .node(1) - .unwrap() - .after(1) - .edges() - .latest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![2, 2]); - }); -} + n1.add_metadata([("lol", Prop::str("smile"))])?; -#[test] -fn node_properties() -> Result<(), GraphError> { - let g = Graph::new(); - - g.add_node( - 0, - 1, - [("t", Prop::str("wallet")), ("cost", Prop::F64(99.5))], - Some("a"), - None, - )?; - - let n1 = g.node(1).unwrap(); - g.add_node(1, 2, [("t", Prop::str("person"))], None, None)?; - g.add_node( - 6, - 3, - [ - ("list_prop", vec![1.1, 2.2, 3.3].into_prop_list()), - ("cost_b", Prop::F64(76.0)), - ], - Some("b"), - None, - )?; - - g.add_node( - 7, - 4, - [ - ("str_prop", Prop::str("hello")), - ("bool_prop", Prop::Bool(true)), - ], - Some("b"), - None, - )?; - - n1.add_metadata([("lol", Prop::str("smile"))])?; - - let node_1_props = n1 - .properties() - .iter() - .filter_map(|(k, v)| v.map(move |v| (k.to_string(), v))) - .collect::>(); - assert_eq!( - node_1_props, - vec![ - ("t".to_string(), Prop::str("wallet")), - ("cost".to_string(), Prop::F64(99.5)), - ] - ); + let node_1_props = n1 + .properties() + .iter() + .filter_map(|(k, v)| v.map(move |v| (k.to_string(), v))) + .collect::>(); + assert_eq!( + node_1_props, + vec![ + ("t".to_string(), Prop::str("wallet")), + ("cost".to_string(), Prop::F64(99.5)), + ] + ); - assert_eq!(n1.metadata().as_vec(), [("lol".into(), "smile".into())]); + assert_eq!(n1.metadata().as_vec(), [("lol".into(), "smile".into())]); - Ok(()) -} + Ok(()) + } -#[test] -fn test_decimal_properties() { - let graph = Graph::new(); - let dec_prop = Prop::Decimal(BigDecimal::new(123456234234123123i64.into(), 9)); - graph - .add_node(0, 1, [("cost".to_string(), dec_prop.clone())], None, None) - .unwrap(); + #[test] + fn test_decimal_properties() { + let graph = Graph::new(); + let dec_prop = Prop::Decimal(BigDecimal::new(123456234234123123i64.into(), 9)); + graph + .add_node(0, 1, [("cost".to_string(), dec_prop.clone())], None, None) + .unwrap(); + + test_storage!(&graph, |graph| { + let node = graph.node(1).unwrap(); + let prop = node.properties().get("cost").unwrap(); + assert_eq!(prop, dec_prop); + }); + } + + #[test] + fn node_history_rows() { + let graph = Graph::new(); + graph + .add_node(1, 2, [("cool".to_string(), Prop::U64(1))], None, None) + .unwrap(); + graph + .add_node(0, 1, [("cool".to_string(), 1u64)], None, None) + .unwrap(); + graph + .add_node( + 1, + 1, + [ + ("coolio".to_string(), Prop::Bool(true)), + ("bla".to_string(), Prop::I64(2)), + ], + None, + None, + ) + .unwrap(); + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + graph + .add_node(1, 1, [("cool".to_string(), 3u64)], None, None) + .unwrap(); - test_storage!(&graph, |graph| { let node = graph.node(1).unwrap(); - let prop = node.properties().get("cost").unwrap(); - assert_eq!(prop, dec_prop); - }); -} -#[test] -fn node_history_rows() { - let graph = Graph::new(); - graph - .add_node(1, 2, [("cool".to_string(), Prop::U64(1))], None, None) - .unwrap(); - graph - .add_node(0, 1, [("cool".to_string(), 1u64)], None, None) - .unwrap(); - graph - .add_node( - 1, - 1, - [ - ("coolio".to_string(), Prop::Bool(true)), - ("bla".to_string(), Prop::I64(2)), - ], - None, - None, - ) - .unwrap(); - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - graph - .add_node(1, 1, [("cool".to_string(), 3u64)], None, None) - .unwrap(); + let actual = node + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); - let node = graph.node(1).unwrap(); + let expected = vec![ + (EventTime::new(0, 1), vec![Prop::U64(1)]), + (EventTime::new(1, 2), vec![Prop::Bool(true), Prop::I64(2)]), + (EventTime::new(1, 4), vec![Prop::U64(3)]), + (EventTime::new(2, 3), vec![]), + ]; - let actual = node - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); + assert_eq!(actual, expected); - let expected = vec![ - (EventTime::new(0, 1), vec![Prop::U64(1)]), - (EventTime::new(1, 2), vec![Prop::Bool(true), Prop::I64(2)]), - (EventTime::new(1, 4), vec![Prop::U64(3)]), - (EventTime::new(2, 3), vec![]), - ]; + let node = graph.node(2).unwrap(); - assert_eq!(actual, expected); + let actual = node + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); - let node = graph.node(2).unwrap(); + let expected = vec![(EventTime::new(1, 0), vec![Prop::U64(1)])]; - let actual = node - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); + assert_eq!(actual, expected); + } - let expected = vec![(EventTime::new(1, 0), vec![Prop::U64(1)])]; + #[test] + fn check_node_history_str() { + let graph = Graph::new(); - assert_eq!(actual, expected); -} + graph + .add_node(4, "Lord Farquaad", NO_PROPS, None, None) + .unwrap(); + graph + .add_node(6, "Lord Farquaad", NO_PROPS, None, None) + .unwrap(); + graph + .add_node(7, "Lord Farquaad", NO_PROPS, None, None) + .unwrap(); + graph + .add_node(8, "Lord Farquaad", NO_PROPS, None, None) + .unwrap(); -#[test] -fn check_node_history_str() { - let graph = Graph::new(); + let times_of_farquaad = graph.node("Lord Farquaad").unwrap().history(); - graph - .add_node(4, "Lord Farquaad", NO_PROPS, None, None) - .unwrap(); - graph - .add_node(6, "Lord Farquaad", NO_PROPS, None, None) - .unwrap(); - graph - .add_node(7, "Lord Farquaad", NO_PROPS, None, None) - .unwrap(); - graph - .add_node(8, "Lord Farquaad", NO_PROPS, None, None) - .unwrap(); + assert_eq!(times_of_farquaad, [4, 6, 7, 8]); - let times_of_farquaad = graph.node("Lord Farquaad").unwrap().history(); + let view = graph.window(1, 8); - assert_eq!(times_of_farquaad, [4, 6, 7, 8]); + let windowed_times_of_farquaad = view.node("Lord Farquaad").unwrap().history(); + assert_eq!(windowed_times_of_farquaad, [4, 6, 7]); + } - let view = graph.window(1, 8); + #[test] + fn check_node_history_num() { + let graph = Graph::new(); - let windowed_times_of_farquaad = view.node("Lord Farquaad").unwrap().history(); - assert_eq!(windowed_times_of_farquaad, [4, 6, 7]); -} + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(3, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(4, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(8, 1, NO_PROPS, None, None).unwrap(); -#[test] -fn check_node_history_num() { - let graph = Graph::new(); + let times_of_one = graph.node(1).unwrap().history(); - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(3, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(4, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(8, 1, NO_PROPS, None, None).unwrap(); + assert_eq!(times_of_one, [1, 2, 3, 4, 8]); - let times_of_one = graph.node(1).unwrap().history(); + let view = graph.window(1, 8); - assert_eq!(times_of_one, [1, 2, 3, 4, 8]); + let windowed_times_of_one = view.node(1).unwrap().history(); + assert_eq!(windowed_times_of_one, [1, 2, 3, 4]); + } - let view = graph.window(1, 8); + #[test] + fn check_edge_history() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(4, 1, 4, NO_PROPS, None).unwrap(); + let times_of_onetwo = graph.edge(1, 2).unwrap().history(); + let times_of_four = graph.edge(1, 4).unwrap().window(1, 5).history(); + let view = graph.window(2, 5); + let windowed_times_of_four = view.edge(1, 4).unwrap().window(2, 4).history(); + + assert_eq!(times_of_onetwo, [1, 3]); + assert_eq!(times_of_four, [4]); + assert!(windowed_times_of_four.is_empty()); + assert_eq!(graph.node(1).unwrap().edge_history_count(), 4); + } - let windowed_times_of_one = view.node(1).unwrap().history(); - assert_eq!(windowed_times_of_one, [1, 2, 3, 4]); -} + #[test] + fn check_node_edge_history_count() { + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(3, 0, 1, NO_PROPS, None).unwrap(); -#[test] -fn check_edge_history() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(4, 1, 4, NO_PROPS, None).unwrap(); - let times_of_onetwo = graph.edge(1, 2).unwrap().history(); - let times_of_four = graph.edge(1, 4).unwrap().window(1, 5).history(); - let view = graph.window(2, 5); - let windowed_times_of_four = view.edge(1, 4).unwrap().window(2, 4).history(); - - assert_eq!(times_of_onetwo, [1, 3]); - assert_eq!(times_of_four, [4]); - assert!(windowed_times_of_four.is_empty()); - assert_eq!(graph.node(1).unwrap().edge_history_count(), 4); -} + let node = graph.node(0).unwrap(); + assert_eq!(node.edge_history_count(), 2); + assert_eq!(node.after(1).edge_history_count(), 1); + assert_eq!(node.after(3).edge_history_count(), 0); + } -#[test] -fn check_node_edge_history_count() { - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(3, 0, 1, NO_PROPS, None).unwrap(); + use raphtory_storage::graph::nodes::node_storage_ops::NodeStorageOps; + + #[test] + fn check_edge_history_on_multiple_shards() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(4, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(5, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(6, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(7, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(8, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(9, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(10, 1, 4, NO_PROPS, None).unwrap(); + + let times_of_onetwo = graph.edge(1, 2).unwrap().history(); + let times_of_four = graph.edge(1, 4).unwrap().window(1, 5).history(); + let times_of_outside_window = graph.edge(1, 4).unwrap().window(1, 4).history(); + let times_of_four_higher = graph.edge(1, 4).unwrap().window(6, 11).history(); + + let view = graph.window(1, 11); + let windowed_times_of_four = view.edge(1, 4).unwrap().window(2, 5).history(); + let windowed_times_of_four_higher = view.edge(1, 4).unwrap().window(8, 11).history(); + + assert_eq!(times_of_onetwo, [1, 3]); + assert_eq!(times_of_four, [4]); + assert_eq!(times_of_four_higher, [6, 7, 8, 9, 10]); + assert!(times_of_outside_window.is_empty()); + assert_eq!(windowed_times_of_four, [4]); + assert_eq!(windowed_times_of_four_higher, [8, 9, 10]); + } - let node = graph.node(0).unwrap(); - assert_eq!(node.edge_history_count(), 2); - assert_eq!(node.after(1).edge_history_count(), 1); - assert_eq!(node.after(3).edge_history_count(), 0); -} + #[derive(Debug)] + struct CustomTime<'a>(&'a str, &'a str); -use raphtory_storage::graph::nodes::node_storage_ops::NodeStorageOps; - -#[test] -fn check_edge_history_on_multiple_shards() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(4, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(5, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(6, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(7, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(8, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(9, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(10, 1, 4, NO_PROPS, None).unwrap(); - - let times_of_onetwo = graph.edge(1, 2).unwrap().history(); - let times_of_four = graph.edge(1, 4).unwrap().window(1, 5).history(); - let times_of_outside_window = graph.edge(1, 4).unwrap().window(1, 4).history(); - let times_of_four_higher = graph.edge(1, 4).unwrap().window(6, 11).history(); - - let view = graph.window(1, 11); - let windowed_times_of_four = view.edge(1, 4).unwrap().window(2, 5).history(); - let windowed_times_of_four_higher = view.edge(1, 4).unwrap().window(8, 11).history(); - - assert_eq!(times_of_onetwo, [1, 3]); - assert_eq!(times_of_four, [4]); - assert_eq!(times_of_four_higher, [6, 7, 8, 9, 10]); - assert!(times_of_outside_window.is_empty()); - assert_eq!(windowed_times_of_four, [4]); - assert_eq!(windowed_times_of_four_higher, [8, 9, 10]); -} + impl TryIntoTimeNeedsEventId for CustomTime<'_> {} + + impl<'a> TryIntoTime for CustomTime<'a> { + fn try_into_time(self) -> Result { + let CustomTime(time, fmt) = self; + let time = NaiveDateTime::parse_from_str(time, fmt)?; + let time = time.and_utc().timestamp_millis(); + Ok(EventTime::from(time)) + } + } -#[derive(Debug)] -struct CustomTime<'a>(&'a str, &'a str); + #[test] + fn test_ingesting_timestamps() { + let earliest_time = "2022-06-06 12:34:00".try_into_time().unwrap().t(); + let latest_time = "2022-06-07 12:34:00".try_into_time().unwrap().t(); -impl TryIntoTimeNeedsEventId for CustomTime<'_> {} + let g = Graph::new(); + g.add_node("2022-06-06T12:34:00.000", 0, NO_PROPS, None, None) + .unwrap(); + g.add_edge("2022-06-07T12:34:00", 1, 2, NO_PROPS, None) + .unwrap(); + assert_eq!(g.earliest_time().unwrap().t(), earliest_time); + assert_eq!(g.latest_time().unwrap().t(), latest_time); + + let g = Graph::new(); + let fmt = "%Y-%m-%d %H:%M"; -impl<'a> TryIntoTime for CustomTime<'a> { - fn try_into_time(self) -> Result { - let CustomTime(time, fmt) = self; - let time = NaiveDateTime::parse_from_str(time, fmt)?; - let time = time.and_utc().timestamp_millis(); - Ok(EventTime::from(time)) + g.add_node(CustomTime("2022-06-06 12:34", fmt), 0, NO_PROPS, None, None) + .unwrap(); + g.add_edge(CustomTime("2022-06-07 12:34", fmt), 1, 2, NO_PROPS, None) + .unwrap(); + assert_eq!(g.earliest_time().unwrap(), earliest_time); + assert_eq!(g.latest_time().unwrap(), latest_time); } -} -#[test] -fn test_ingesting_timestamps() { - let earliest_time = "2022-06-06 12:34:00".try_into_time().unwrap().t(); - let latest_time = "2022-06-07 12:34:00".try_into_time().unwrap().t(); + #[test] + fn test_prop_display_str() { + let mut prop = Prop::Str("hello".into()); + assert_eq!(format!("{}", prop), "hello"); - let g = Graph::new(); - g.add_node("2022-06-06T12:34:00.000", 0, NO_PROPS, None, None) - .unwrap(); - g.add_edge("2022-06-07T12:34:00", 1, 2, NO_PROPS, None) - .unwrap(); - assert_eq!(g.earliest_time().unwrap().t(), earliest_time); - assert_eq!(g.latest_time().unwrap().t(), latest_time); + prop = Prop::I32(42); + assert_eq!(format!("{}", prop), "42"); - let g = Graph::new(); - let fmt = "%Y-%m-%d %H:%M"; + prop = Prop::I64(9223372036854775807); + assert_eq!(format!("{}", prop), "9223372036854775807"); - g.add_node(CustomTime("2022-06-06 12:34", fmt), 0, NO_PROPS, None, None) - .unwrap(); - g.add_edge(CustomTime("2022-06-07 12:34", fmt), 1, 2, NO_PROPS, None) - .unwrap(); - assert_eq!(g.earliest_time().unwrap(), earliest_time); - assert_eq!(g.latest_time().unwrap(), latest_time); -} + prop = Prop::U32(4294967295); + assert_eq!(format!("{}", prop), "4294967295"); + + prop = Prop::U64(18446744073709551615); + assert_eq!(format!("{}", prop), "18446744073709551615"); -#[test] -fn test_prop_display_str() { - let mut prop = Prop::Str("hello".into()); - assert_eq!(format!("{}", prop), "hello"); + prop = Prop::U8(255); + assert_eq!(format!("{}", prop), "255"); - prop = Prop::I32(42); - assert_eq!(format!("{}", prop), "42"); + prop = Prop::U16(65535); + assert_eq!(format!("{}", prop), "65535"); - prop = Prop::I64(9223372036854775807); - assert_eq!(format!("{}", prop), "9223372036854775807"); + prop = Prop::F32(3.15159); + assert_eq!(format!("{}", prop), "3.15159"); - prop = Prop::U32(4294967295); - assert_eq!(format!("{}", prop), "4294967295"); + prop = Prop::F64(3.151592653589793); + assert_eq!(format!("{}", prop), "3.151592653589793"); - prop = Prop::U64(18446744073709551615); - assert_eq!(format!("{}", prop), "18446744073709551615"); + prop = Prop::Bool(true); + assert_eq!(format!("{}", prop), "true"); + } - prop = Prop::U8(255); - assert_eq!(format!("{}", prop), "255"); + #[test] + fn test_graph_metadata_proptest() { + proptest!(|(u64_props: HashMap)| { + let g = Graph::new(); - prop = Prop::U16(65535); - assert_eq!(format!("{}", prop), "65535"); + let as_props = u64_props + .into_iter() + .map(|(name, value)| (name, Prop::U64(value))) + .collect::>(); - prop = Prop::F32(3.15159); - assert_eq!(format!("{}", prop), "3.15159"); + g.add_metadata(as_props.clone()).unwrap(); - prop = Prop::F64(3.151592653589793); - assert_eq!(format!("{}", prop), "3.151592653589793"); + let props_map = as_props.into_iter().collect::>(); - prop = Prop::Bool(true); - assert_eq!(format!("{}", prop), "true"); -} + prop_assert!(props_map + .into_iter() + .all(|(name, value)| g.metadata().get(&name).unwrap() == value)); + }); + } -#[test] -fn test_graph_metadata_proptest() { - proptest!(|(u64_props: HashMap)| { + #[test] + fn test_graph_metadata() { let g = Graph::new(); - let as_props = u64_props - .into_iter() - .map(|(name, value)| (name, Prop::U64(value))) - .collect::>(); + let as_props: Vec<(&str, Prop)> = + vec![("mylist", Prop::list(vec![Prop::I64(1), Prop::I64(2)]))]; g.add_metadata(as_props.clone()).unwrap(); - let props_map = as_props.into_iter().collect::>(); - - prop_assert!(props_map + let props_names = as_props .into_iter() - .all(|(name, value)| g.metadata().get(&name).unwrap() == value)); - }); -} - -#[test] -fn test_graph_metadata() { - let g = Graph::new(); + .map(|(name, _)| name.into()) + .collect::>(); - let as_props: Vec<(&str, Prop)> = - vec![("mylist", Prop::list(vec![Prop::I64(1), Prop::I64(2)]))]; + assert_eq!(g.metadata().keys().collect::>(), props_names); - g.add_metadata(as_props.clone()).unwrap(); + let data = vec![ + ("key1", Prop::I64(10)), + ("key2", Prop::I64(20)), + ("key3", Prop::I64(30)), + ]; + let as_props: Vec<(&str, Prop)> = vec![("mylist2", Prop::map(data))]; - let props_names = as_props - .into_iter() - .map(|(name, _)| name.into()) - .collect::>(); + g.add_metadata(as_props.clone()).unwrap(); - assert_eq!(g.metadata().keys().collect::>(), props_names); + let props_names2: HashSet = as_props + .into_iter() + .map(|(name, _)| name.into()) + .collect::>(); - let data = vec![ - ("key1", Prop::I64(10)), - ("key2", Prop::I64(20)), - ("key3", Prop::I64(30)), - ]; - let as_props: Vec<(&str, Prop)> = vec![("mylist2", Prop::map(data))]; + assert_eq!( + g.metadata().keys().collect::>(), + props_names + .union(&props_names2) + .cloned() + .collect::>() + ); + } - g.add_metadata(as_props.clone()).unwrap(); + #[test] + fn test_add_graph_metadata_with_existing_key_throws_error() { + let g = Graph::new(); + g.add_metadata(vec![("style", Prop::str("red"))]).unwrap(); - let props_names2: HashSet = as_props - .into_iter() - .map(|(name, _)| name.into()) - .collect::>(); - - assert_eq!( - g.metadata().keys().collect::>(), - props_names - .union(&props_names2) - .cloned() - .collect::>() - ); -} + assert!(g.add_metadata(vec![("style", Prop::str("blue"))]).is_err()); + assert_eq!(g.metadata().get("style").unwrap(), Prop::str("red")); // Value is unchanged + } -#[test] -fn test_add_graph_metadata_with_existing_key_throws_error() { - let g = Graph::new(); - g.add_metadata(vec![("style", Prop::str("red"))]).unwrap(); + #[test] + fn test_graph_metadata_with_maps() { + let g = Graph::new(); - assert!(g.add_metadata(vec![("style", Prop::str("blue"))]).is_err()); - assert_eq!(g.metadata().get("style").unwrap(), Prop::str("red")); // Value is unchanged -} + let style_with_size = Prop::map(vec![("fill", Prop::str("red")), ("size", Prop::I64(5))]); -#[test] -fn test_graph_metadata_with_maps() { - let g = Graph::new(); + let style_with_opacity = Prop::map(vec![ + ("fill", Prop::str("red")), + ("opacity", Prop::F64(0.4)), + ]); - let style_with_size = Prop::map(vec![("fill", Prop::str("red")), ("size", Prop::I64(5))]); + // Add first metadata and verify + g.add_metadata(vec![("style", style_with_size.clone())]) + .unwrap(); + let actual = g.metadata().get("style").unwrap(); + assert_eq!(actual, style_with_size.clone()); - let style_with_opacity = Prop::map(vec![ - ("fill", Prop::str("red")), - ("opacity", Prop::F64(0.4)), - ]); + // Update metadata and verify + g.update_metadata(vec![("style", style_with_opacity.clone())]) + .unwrap(); + let actual = g.metadata().get("style").unwrap(); + assert_eq!(actual, style_with_opacity.clone()); + + // Add another metadata property and verify + let config = Prop::map(vec![ + ("theme", Prop::str("dark")), + ("version", Prop::I64(2)), + ]); + g.add_metadata(vec![("config", config.clone())]).unwrap(); + let actual_config = g.metadata().get("config").unwrap(); + assert_eq!(actual_config, config.clone()); + + // Verify style is still the updated value + let actual_style = g.metadata().get("style").unwrap(); + assert_eq!(actual_style, style_with_opacity.clone()); + + // Verify all metadata keys exist + let keys: Vec<_> = g.metadata().keys().sorted().collect(); + assert_eq!(keys, vec!["config", "style"]); + } - // Add first metadata and verify - g.add_metadata(vec![("style", style_with_size.clone())]) - .unwrap(); - let actual = g.metadata().get("style").unwrap(); - assert_eq!(actual, style_with_size.clone()); + #[test] + fn test_graph_metadata_names() { + proptest!(|(u64_props: HashMap)| { + let g = Graph::new(); - // Update metadata and verify - g.update_metadata(vec![("style", style_with_opacity.clone())]) - .unwrap(); - let actual = g.metadata().get("style").unwrap(); - assert_eq!(actual, style_with_opacity.clone()); - - // Add another metadata property and verify - let config = Prop::map(vec![ - ("theme", Prop::str("dark")), - ("version", Prop::I64(2)), - ]); - g.add_metadata(vec![("config", config.clone())]).unwrap(); - let actual_config = g.metadata().get("config").unwrap(); - assert_eq!(actual_config, config.clone()); - - // Verify style is still the updated value - let actual_style = g.metadata().get("style").unwrap(); - assert_eq!(actual_style, style_with_opacity.clone()); - - // Verify all metadata keys exist - let keys: Vec<_> = g.metadata().keys().sorted().collect(); - assert_eq!(keys, vec!["config", "style"]); -} + let as_props = u64_props + .into_iter() + .map(|(name, value)| (name.into(), Prop::U64(value))) + .collect::>(); -#[test] -fn test_graph_metadata_names() { - proptest!(|(u64_props: HashMap)| { - let g = Graph::new(); + g.add_metadata(as_props.clone()).unwrap(); - let as_props = u64_props - .into_iter() - .map(|(name, value)| (name.into(), Prop::U64(value))) - .collect::>(); + let props_names = as_props + .into_iter() + .map(|(name, _)| name) + .collect::>(); - g.add_metadata(as_props.clone()).unwrap(); - - let props_names = as_props - .into_iter() - .map(|(name, _)| name) - .collect::>(); - - prop_assert_eq!(g.metadata().keys().collect::>(), props_names); - }); -} + prop_assert_eq!(g.metadata().keys().collect::>(), props_names); + }); + } -#[test] -fn test_graph_temporal_props() { - proptest!(|(str_props: HashMap)| { - global_info_logger(); + #[test] + fn test_graph_temporal_props() { + proptest!(|(str_props: HashMap)| { + global_info_logger(); - let g = Graph::new(); - let (t0, t1) = (1, 2); + let g = Graph::new(); + let (t0, t1) = (1, 2); - // Split properties into two sets based on even/odd index - // Even-indexed properties go to t0, odd-indexed to t1 - let mut t0_props = HashMap::new(); - let mut t1_props = HashMap::new(); + // Split properties into two sets based on even/odd index + // Even-indexed properties go to t0, odd-indexed to t1 + let mut t0_props = HashMap::new(); + let mut t1_props = HashMap::new(); - for (i, (name, value)) in str_props.iter().enumerate() { - let prop_name: ArcStr = name.as_str().into(); - let prop_value = Prop::from(value.as_str()); + for (i, (name, value)) in str_props.iter().enumerate() { + let prop_name: ArcStr = name.as_str().into(); + let prop_value = Prop::from(value.as_str()); - if i % 2 == 0 { - t0_props.insert(prop_name, prop_value); - } else { - t1_props.insert(prop_name, prop_value); + if i % 2 == 0 { + t0_props.insert(prop_name, prop_value); + } else { + t1_props.insert(prop_name, prop_value); + } } - } - g.add_properties(t0, t0_props.clone()).unwrap(); - g.add_properties(t1, t1_props.clone()).unwrap(); - - // Verify properties can be retrieved at their timestamps - for (name, expected_value) in t0_props.iter() { - let actual = g.properties().temporal().get(name).unwrap().at(t0); - - prop_assert_eq!( - actual, - Some(expected_value.clone()), - "Property '{}' at t0 has wrong value", - name - ); - } + g.add_properties(t0, t0_props.clone()).unwrap(); + g.add_properties(t1, t1_props.clone()).unwrap(); - for (name, expected_value) in t1_props.iter() { - let actual_value = g.properties().temporal().get(name).unwrap().at(t1); + // Verify properties can be retrieved at their timestamps + for (name, expected_value) in t0_props.iter() { + let actual = g.properties().temporal().get(name).unwrap().at(t0); - prop_assert_eq!( - actual_value, - Some(expected_value.clone()), - "Property '{}' at t1 has wrong value", - name - ); - } + prop_assert_eq!( + actual, + Some(expected_value.clone()), + "Property '{}' at t0 has wrong value", + name + ); + } - // Verify iter_latest returns all t0 properties - let actual_t0_props: HashMap<_, _> = g - .at(t0) - .properties() - .temporal() - .iter_latest() - .map(|(prop_name, prop_value)| (prop_name.clone(), prop_value)) - .collect(); + for (name, expected_value) in t1_props.iter() { + let actual_value = g.properties().temporal().get(name).unwrap().at(t1); - prop_assert_eq!( - actual_t0_props, - t0_props, - "iter_latest() at t0 returned wrong properties" - ); + prop_assert_eq!( + actual_value, + Some(expected_value.clone()), + "Property '{}' at t1 has wrong value", + name + ); + } - // Verify latest returns correct values for t1 properties - for (name, expected_value) in t1_props.iter() { - let actual = g - .at(t1) + // Verify iter_latest returns all t0 properties + let actual_t0_props: HashMap<_, _> = g + .at(t0) .properties() .temporal() - .get(name) - .and_then(|v| v.latest()); + .iter_latest() + .map(|(prop_name, prop_value)| (prop_name.clone(), prop_value)) + .collect(); prop_assert_eq!( - actual, - Some(expected_value.clone()), - "Property '{}' latest() at t1 has wrong value", - name + actual_t0_props, + t0_props, + "iter_latest() at t0 returned wrong properties" ); - } - }); -} - -#[test] -fn test_graph_temporal_props_with_maps() { - let g = Graph::new(); - let style_with_size = Prop::map(vec![("fill", Prop::str("red")), ("size", Prop::I64(5))]); + // Verify latest returns correct values for t1 properties + for (name, expected_value) in t1_props.iter() { + let actual = g + .at(t1) + .properties() + .temporal() + .get(name) + .and_then(|v| v.latest()); + + prop_assert_eq!( + actual, + Some(expected_value.clone()), + "Property '{}' latest() at t1 has wrong value", + name + ); + } + }); + } - let style_with_opacity = Prop::map(vec![ - ("fill", Prop::str("red")), - ("opacity", Prop::F64(0.4)), - ]); + #[test] + fn test_graph_temporal_props_with_maps() { + let g = Graph::new(); - // Add temporal properties with nested maps at different timestamps - g.add_properties(0, vec![("style", style_with_size.clone())]) - .unwrap(); - g.add_properties(1, vec![("style", style_with_opacity.clone())]) - .unwrap(); - g.add_properties(2, vec![("style", style_with_size.clone())]) - .unwrap(); - g.add_properties(3, vec![("style", style_with_opacity.clone())]) - .unwrap(); + let style_with_size = Prop::map(vec![("fill", Prop::str("red")), ("size", Prop::I64(5))]); - // Verify properties can be retrieved at their timestamps - let actual_t0 = g.properties().temporal().get("style").unwrap().at(0); - assert_eq!(actual_t0, Some(style_with_size.clone())); + let style_with_opacity = Prop::map(vec![ + ("fill", Prop::str("red")), + ("opacity", Prop::F64(0.4)), + ]); - let actual_t1 = g.properties().temporal().get("style").unwrap().at(1); - assert_eq!(actual_t1, Some(style_with_opacity.clone())); + // Add temporal properties with nested maps at different timestamps + g.add_properties(0, vec![("style", style_with_size.clone())]) + .unwrap(); + g.add_properties(1, vec![("style", style_with_opacity.clone())]) + .unwrap(); + g.add_properties(2, vec![("style", style_with_size.clone())]) + .unwrap(); + g.add_properties(3, vec![("style", style_with_opacity.clone())]) + .unwrap(); - let actual_t2 = g.properties().temporal().get("style").unwrap().at(2); - assert_eq!(actual_t2, Some(style_with_size.clone())); + // Verify properties can be retrieved at their timestamps + let actual_t0 = g.properties().temporal().get("style").unwrap().at(0); + assert_eq!(actual_t0, Some(style_with_size.clone())); - let actual_t3 = g.properties().temporal().get("style").unwrap().at(3); - assert_eq!(actual_t3, Some(style_with_opacity.clone())); + let actual_t1 = g.properties().temporal().get("style").unwrap().at(1); + assert_eq!(actual_t1, Some(style_with_opacity.clone())); - // Verify history returns all timestamps - let history: Vec<_> = g - .properties() - .temporal() - .get("style") - .unwrap() - .history() - .collect(); + let actual_t2 = g.properties().temporal().get("style").unwrap().at(2); + assert_eq!(actual_t2, Some(style_with_size.clone())); - assert_eq!(history, vec![0, 1, 2, 3]); -} + let actual_t3 = g.properties().temporal().get("style").unwrap().at(3); + assert_eq!(actual_t3, Some(style_with_opacity.clone())); -#[test] -fn test_temporal_edge_props_window() { - let graph = Graph::new(); - graph - .add_edge(1, 1, 2, vec![("weight".to_string(), Prop::I64(1))], None) - .unwrap(); - graph - .add_edge(2, 1, 2, vec![("weight".to_string(), Prop::I64(2))], None) - .unwrap(); - graph - .add_edge(3, 1, 2, vec![("weight".to_string(), Prop::I64(3))], None) - .unwrap(); - test_storage!(&graph, |graph| { - let e = graph - .node(1) - .unwrap() - .out_edges() - .into_iter() - .next() - .unwrap(); - let res: HashMap> = e - .window(1, 3) + // Verify history returns all timestamps + let history: Vec<_> = g .properties() .temporal() - .iter() - .map(|(k, v)| (k.clone(), v.iter().map(|(x, y)| (x.t(), y)).collect())) + .get("style") + .unwrap() + .history() .collect(); - let mut exp = HashMap::new(); - exp.insert( - ArcStr::from("weight"), - vec![(1, Prop::I64(1)), (2, Prop::I64(2))], - ); - assert_eq!(res, exp); - }); -} - -#[test] -fn test_node_early_late_times() { - let graph = Graph::new(); - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(3, 1, NO_PROPS, None, None).unwrap(); - - // FIXME: Node add without properties not showing up (Issue #46) - assert_eq!(graph.node(1).unwrap().earliest_time().unwrap().t(), 1); - assert_eq!(graph.node(1).unwrap().latest_time().unwrap().t(), 3); - - assert_eq!(graph.at(2).node(1).unwrap().earliest_time().unwrap().t(), 2); - assert_eq!(graph.at(2).node(1).unwrap().latest_time().unwrap().t(), 2); + assert_eq!(history, vec![0, 1, 2, 3]); + } - assert_eq!( + #[test] + fn test_temporal_edge_props_window() { + let graph = Graph::new(); graph - .before(2) - .node(1) - .unwrap() - .earliest_time() - .unwrap() - .t(), - 1 - ); - assert_eq!( - graph.before(2).node(1).unwrap().latest_time().unwrap().t(), - 1 - ); - - assert_eq!( - graph.after(2).node(1).unwrap().earliest_time().unwrap().t(), - 3 - ); - assert_eq!( - graph.after(2).node(1).unwrap().latest_time().unwrap().t(), - 3 - ); -} - -#[test] -fn test_node_ids() { - let graph = Graph::new(); - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 3, NO_PROPS, None, None).unwrap(); - - assert_eq!(graph.nodes().id().sort_by_id(), vec![1u64, 2u64, 3u64]); - - let g_at = graph.at(1); - assert_eq!(g_at.nodes().id().sort_by_id(), vec![1u64, 2u64]); -} - -#[test] -fn test_edge_layer_name() -> Result<(), GraphError> { - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None)?; - graph.add_edge(0, 0, 1, NO_PROPS, Some("awesome name"))?; + .add_edge(1, 1, 2, vec![("weight".to_string(), Prop::I64(1))], None) + .unwrap(); + graph + .add_edge(2, 1, 2, vec![("weight".to_string(), Prop::I64(2))], None) + .unwrap(); + graph + .add_edge(3, 1, 2, vec![("weight".to_string(), Prop::I64(3))], None) + .unwrap(); + test_storage!(&graph, |graph| { + let e = graph + .node(1) + .unwrap() + .out_edges() + .into_iter() + .next() + .unwrap(); + let res: HashMap> = e + .window(1, 3) + .properties() + .temporal() + .iter() + .map(|(k, v)| (k.clone(), v.iter().map(|(x, y)| (x.t(), y)).collect())) + .collect(); + + let mut exp = HashMap::new(); + exp.insert( + ArcStr::from("weight"), + vec![(1, Prop::I64(1)), (2, Prop::I64(2))], + ); + assert_eq!(res, exp); + }); + } - test_storage!(&graph, |graph| { - let what = graph.edges().id().collect_vec(); - assert_eq!(what, vec![(0u64.into(), 1u64.into())]); + #[test] + fn test_node_early_late_times() { + let graph = Graph::new(); + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(3, 1, NO_PROPS, None, None).unwrap(); - let layer_names = graph.edges().layer_names().flatten().sorted().collect_vec(); - assert_eq!(layer_names, vec!["_default", "awesome name"]); - }); - Ok(()) -} + // FIXME: Node add without properties not showing up (Issue #46) + assert_eq!(graph.node(1).unwrap().earliest_time().unwrap().t(), 1); + assert_eq!(graph.node(1).unwrap().latest_time().unwrap().t(), 3); -#[test] -fn test_edge_from_single_layer() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer")).unwrap(); + assert_eq!(graph.at(2).node(1).unwrap().earliest_time().unwrap().t(), 2); + assert_eq!(graph.at(2).node(1).unwrap().latest_time().unwrap().t(), 2); - test_storage!(&graph, |graph| { - assert!(graph.edge(1, 2).is_some()); - assert!(graph.layers("layer").unwrap().edge(1, 2).is_some()) - }); -} + assert_eq!( + graph + .before(2) + .node(1) + .unwrap() + .earliest_time() + .unwrap() + .t(), + 1 + ); + assert_eq!( + graph.before(2).node(1).unwrap().latest_time().unwrap().t(), + 1 + ); -#[test] -fn test_edge_layer_intersect_layer() { - let graph = Graph::new(); + assert_eq!( + graph.after(2).node(1).unwrap().earliest_time().unwrap().t(), + 3 + ); + assert_eq!( + graph.after(2).node(1).unwrap().latest_time().unwrap().t(), + 3 + ); + } - graph - .add_edge(1, 1, 2, NO_PROPS, Some("layer1")) - .expect("add edge"); - graph - .add_edge(1, 1, 3, NO_PROPS, Some("layer3")) - .expect("add edge"); - graph.add_edge(1, 1, 4, NO_PROPS, None).expect("add edge"); + #[test] + fn test_node_ids() { + let graph = Graph::new(); + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 3, NO_PROPS, None, None).unwrap(); - test_storage!(&graph, |graph| { - let g_layers = graph.layers(vec!["layer1", "layer3"]).expect("layer"); + assert_eq!(graph.nodes().id().sort_by_id(), vec![1u64, 2u64, 3u64]); - assert!(g_layers.layers("layer1").unwrap().edge(1, 2).is_some()); - assert!(g_layers.layers("layer3").unwrap().edge(1, 3).is_some()); - assert!(g_layers.edge(1, 2).is_some()); - assert!(g_layers.edge(1, 3).is_some()); + let g_at = graph.at(1); + assert_eq!(g_at.nodes().id().sort_by_id(), vec![1u64, 2u64]); + } - assert!(g_layers.edge(1, 4).is_none()); + #[test] + fn test_edge_layer_name() -> Result<(), GraphError> { + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None)?; + graph.add_edge(0, 0, 1, NO_PROPS, Some("awesome name"))?; - let one = g_layers.node(1).expect("node"); - let ns = one - .neighbours() - .iter() - .filter_map(|v| v.id().as_u64()) - .collect::>(); - assert_eq!(ns, vec![2, 3]); + test_storage!(&graph, |graph| { + let what = graph.edges().id().collect_vec(); + assert_eq!(what, vec![(0u64.into(), 1u64.into())]); - let g_layers2 = g_layers.layers(vec!["layer1"]).expect("layer"); + let layer_names = graph.edges().layer_names().flatten().sorted().collect_vec(); + assert_eq!(layer_names, vec!["_default", "awesome name"]); + }); + Ok(()) + } - assert!(g_layers2.layers("layer1").unwrap().edge(1, 2).is_some()); - assert!(g_layers2.edge(1, 2).is_some()); + #[test] + fn test_edge_from_single_layer() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer")).unwrap(); - assert!(g_layers2.edge(1, 3).is_none()); + test_storage!(&graph, |graph| { + assert!(graph.edge(1, 2).is_some()); + assert!(graph.layers("layer").unwrap().edge(1, 2).is_some()) + }); + } - assert!(g_layers2.edge(1, 4).is_none()); + #[test] + fn test_edge_layer_intersect_layer() { + let graph = Graph::new(); - let one = g_layers2.node(1).expect("node"); - let ns = one - .neighbours() - .iter() - .filter_map(|v| v.id().as_u64()) - .collect::>(); - assert_eq!(ns, vec![2]); - }); -} + graph + .add_edge(1, 1, 2, NO_PROPS, Some("layer1")) + .expect("add edge"); + graph + .add_edge(1, 1, 3, NO_PROPS, Some("layer3")) + .expect("add edge"); + graph.add_edge(1, 1, 4, NO_PROPS, None).expect("add edge"); -#[test] -fn simple_triangle() { - let graph = Graph::new(); + test_storage!(&graph, |graph| { + let g_layers = graph.layers(vec!["layer1", "layer3"]).expect("layer"); - let vs = vec![(1, 1, 2), (2, 1, 3), (3, 2, 1), (4, 3, 2)]; + assert!(g_layers.layers("layer1").unwrap().edge(1, 2).is_some()); + assert!(g_layers.layers("layer3").unwrap().edge(1, 3).is_some()); + assert!(g_layers.edge(1, 2).is_some()); + assert!(g_layers.edge(1, 3).is_some()); - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } + assert!(g_layers.edge(1, 4).is_none()); - test_storage!(&graph, |graph| { - let windowed_graph = graph.window(0, 5); - let one = windowed_graph.node(1).expect("node"); - let ns_win = one - .neighbours() - .id() - .filter_map(|id| id.to_u64()) - .collect::>(); + let one = g_layers.node(1).expect("node"); + let ns = one + .neighbours() + .iter() + .filter_map(|v| v.id().as_u64()) + .collect::>(); + assert_eq!(ns, vec![2, 3]); - let one = graph.node(1).expect("node"); - let ns = one - .neighbours() - .id() - .filter_map(|id| id.to_u64()) - .collect::>(); - assert_eq!(ns, vec![2, 3]); - assert_eq!(ns_win, ns); - }); -} + let g_layers2 = g_layers.layers(vec!["layer1"]).expect("layer"); -#[test] -fn test_layer_explode() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + assert!(g_layers2.layers("layer1").unwrap().edge(1, 2).is_some()); + assert!(g_layers2.edge(1, 2).is_some()); - test_storage!(&graph, |graph| { - let e = graph.edge(1, 2).expect("edge"); + assert!(g_layers2.edge(1, 3).is_none()); - let layer_exploded = e - .explode_layers() - .iter() - .filter_map(|e| { - e.edge - .layer() - .and_then(|layer| Some((e.src().id().as_u64()?, e.dst().id().as_u64()?, layer))) - }) - .collect::>(); + assert!(g_layers2.edge(1, 4).is_none()); - assert_eq!( - layer_exploded, - vec![ - (1u64, 2u64, LayerId(1)), - (1u64, 2u64, LayerId(2)), - (1u64, 2u64, LayerId(3)), - ] - ); - }); -} + let one = g_layers2.node(1).expect("node"); + let ns = one + .neighbours() + .iter() + .filter_map(|v| v.id().as_u64()) + .collect::>(); + assert_eq!(ns, vec![2]); + }); + } -#[test] -fn test_layer_explode_window() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + #[test] + fn simple_triangle() { + let graph = Graph::new(); - test_storage!(&graph, |graph| { - let g = graph.window(0, 3); - let e = g.edge(1, 2).expect("edge"); + let vs = vec![(1, 1, 2), (2, 1, 3), (3, 2, 1), (4, 3, 2)]; - let layer_exploded = e - .explode_layers() - .iter() - .filter_map(|e| { - e.layer_name() - .ok() - .map(|layer| (e.src().id(), e.dst().id(), layer)) - }) - .collect::>(); + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } - assert_eq!( - layer_exploded, - vec![ - (GID::U64(1), GID::U64(2), ArcStr::from("layer1")), - (GID::U64(1), GID::U64(2), ArcStr::from("layer2")) - ] - ); - }); -} + test_storage!(&graph, |graph| { + let windowed_graph = graph.window(0, 5); + let one = windowed_graph.node(1).expect("node"); + let ns_win = one + .neighbours() + .id() + .filter_map(|id| id.to_u64()) + .collect::>(); + + let one = graph.node(1).expect("node"); + let ns = one + .neighbours() + .id() + .filter_map(|id| id.to_u64()) + .collect::>(); + assert_eq!(ns, vec![2, 3]); + assert_eq!(ns_win, ns); + }); + } -#[test] -fn test_layer_explode_stacking() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + #[test] + fn test_layer_explode() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let e = graph.edge(1, 2).expect("edge"); + + let layer_exploded = e + .explode_layers() + .iter() + .filter_map(|e| { + e.edge.layer().and_then(|layer| { + Some((e.src().id().as_u64()?, e.dst().id().as_u64()?, layer)) + }) + }) + .collect::>(); - test_storage!(&graph, |graph| { - let e = graph.edge(1, 2).expect("edge"); + assert_eq!( + layer_exploded, + vec![ + (1u64, 2u64, LayerId(1)), + (1u64, 2u64, LayerId(2)), + (1u64, 2u64, LayerId(3)), + ] + ); + }); + } - let layer_exploded = e - .explode_layers() - .iter() - .flat_map(|e| { - e.explode().into_iter().filter_map(|e| { + #[test] + fn test_layer_explode_window() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let g = graph.window(0, 3); + let e = g.edge(1, 2).expect("edge"); + + let layer_exploded = e + .explode_layers() + .iter() + .filter_map(|e| { e.layer_name() .ok() - .zip(e.time().ok().map(|t| t.t())) - .map(|(layer, t)| (t, e.src().id(), e.dst().id(), layer)) + .map(|layer| (e.src().id(), e.dst().id(), layer)) }) - }) - .collect::>(); + .collect::>(); - assert_eq!( - layer_exploded, - vec![ - (0, 1, 2, "layer1"), - (2, 1, 2, "layer1"), - (1, 1, 2, "layer2"), - (3, 1, 2, "_default"), - ] - .into_iter() - .map(|(a, b, c, d)| (a, GID::U64(b), GID::U64(c), ArcStr::from(d))) - .collect::>() - ); - }); -} + assert_eq!( + layer_exploded, + vec![ + (GID::U64(1), GID::U64(2), ArcStr::from("layer1")), + (GID::U64(1), GID::U64(2), ArcStr::from("layer2")) + ] + ); + }); + } -#[test] -fn test_layer_explode_stacking_window() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + #[test] + fn test_layer_explode_stacking() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let e = graph.edge(1, 2).expect("edge"); + + let layer_exploded = e + .explode_layers() + .iter() + .flat_map(|e| { + e.explode().into_iter().filter_map(|e| { + e.layer_name() + .ok() + .zip(e.time().ok().map(|t| t.t())) + .map(|(layer, t)| (t, e.src().id(), e.dst().id(), layer)) + }) + }) + .collect::>(); - test_storage!(&graph, |graph| { - let g = graph.window(0, 3); - let e = g.edge(1, 2).expect("edge"); + assert_eq!( + layer_exploded, + vec![ + (0, 1, 2, "layer1"), + (2, 1, 2, "layer1"), + (1, 1, 2, "layer2"), + (3, 1, 2, "_default"), + ] + .into_iter() + .map(|(a, b, c, d)| (a, GID::U64(b), GID::U64(c), ArcStr::from(d))) + .collect::>() + ); + }); + } - let layer_exploded = e - .explode_layers() - .iter() - .flat_map(|e| { - e.explode().into_iter().filter_map(|e| { - e.layer_name() - .ok() - .zip(e.time().ok().map(|t| t.t())) - .map(|(layer, t)| (t, e.src().id(), e.dst().id(), layer)) + #[test] + fn test_layer_explode_stacking_window() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let g = graph.window(0, 3); + let e = g.edge(1, 2).expect("edge"); + + let layer_exploded = e + .explode_layers() + .iter() + .flat_map(|e| { + e.explode().into_iter().filter_map(|e| { + e.layer_name() + .ok() + .zip(e.time().ok().map(|t| t.t())) + .map(|(layer, t)| (t, e.src().id(), e.dst().id(), layer)) + }) }) - }) - .collect::>(); + .collect::>(); - assert_eq!( - layer_exploded, - vec![ - (0, 1, 2, "layer1"), - (2, 1, 2, "layer1"), - (1, 1, 2, "layer2") - ] - .into_iter() - .map(|(a, b, c, d)| { (a, GID::U64(b), GID::U64(c), ArcStr::from(d)) }) - .collect::>() - ); - }); -} + assert_eq!( + layer_exploded, + vec![ + (0, 1, 2, "layer1"), + (2, 1, 2, "layer1"), + (1, 1, 2, "layer2") + ] + .into_iter() + .map(|(a, b, c, d)| { (a, GID::U64(b), GID::U64(c), ArcStr::from(d)) }) + .collect::>() + ); + }); + } -#[test] -fn test_multi_layer_degree() { - let graph = Graph::new(); - graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 3, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); - - graph.add_edge(1, 1, 4, NO_PROPS, None).expect("failed"); - graph - .add_edge(1, 1, 2, NO_PROPS, "eth".into()) - .expect("failed"); - graph - .add_edge(1, 1, 3, NO_PROPS, "eth".into()) - .expect("failed"); - - graph - .add_edge(1, 2, 3, NO_PROPS, "eth".into()) - .expect("failed"); - graph.add_edge(1, 4, 3, NO_PROPS, None).expect("failed"); - - test_storage!(&graph, |graph| { - let actual = graph.node(1u64).map(|n| n.out_degree()); - assert_eq!(actual, Some(3)); - - let actual = graph.node(3u64).map(|n| n.in_degree()); - assert_eq!(actual, Some(3)); - - let actual = graph.node(3u64).map(|n| n.degree()); - assert_eq!(actual, Some(3)); - }); -} + #[test] + fn test_multi_layer_degree() { + let graph = Graph::new(); + graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 3, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); -#[test] -fn test_multiple_layers_fundamentals() { - let graph = Graph::new(); - - graph - .add_edge(1, 1, 2, [("tx_sent", 10u64)], "btc".into()) - .expect("failed"); - graph - .add_edge(1, 1, 2, [("tx_sent", 20u64)], "eth".into()) - .expect("failed"); - graph - .add_edge(1, 1, 2, [("tx_sent", 70u64)], "tether".into()) - .expect("failed"); - - test_storage!(&graph, |graph| { - let e = graph.edge(1, 2).expect("failed to get edge"); - let sum: u64 = e - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum(); + graph.add_edge(1, 1, 4, NO_PROPS, None).expect("failed"); + graph + .add_edge(1, 1, 2, NO_PROPS, "eth".into()) + .expect("failed"); + graph + .add_edge(1, 1, 3, NO_PROPS, "eth".into()) + .expect("failed"); - assert_eq!(sum, 100); + graph + .add_edge(1, 2, 3, NO_PROPS, "eth".into()) + .expect("failed"); + graph.add_edge(1, 4, 3, NO_PROPS, None).expect("failed"); - let lg = graph - .layers(vec!["eth", "btc"]) - .expect("failed to layer graph"); + test_storage!(&graph, |graph| { + let actual = graph.node(1u64).map(|n| n.out_degree()); + assert_eq!(actual, Some(3)); - let e = lg.edge(1, 2).expect("failed to get edge"); + let actual = graph.node(3u64).map(|n| n.in_degree()); + assert_eq!(actual, Some(3)); - let sum_eth_btc: u64 = e - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum(); + let actual = graph.node(3u64).map(|n| n.degree()); + assert_eq!(actual, Some(3)); + }); + } - assert_eq!(sum_eth_btc, 30); + #[test] + fn test_multiple_layers_fundamentals() { + let graph = Graph::new(); - assert_eq!(lg.count_edges(), 1); + graph + .add_edge(1, 1, 2, [("tx_sent", 10u64)], "btc".into()) + .expect("failed"); + graph + .add_edge(1, 1, 2, [("tx_sent", 20u64)], "eth".into()) + .expect("failed"); + graph + .add_edge(1, 1, 2, [("tx_sent", 70u64)], "tether".into()) + .expect("failed"); - let e = graph.edge(1, 2).expect("failed to get edge"); + test_storage!(&graph, |graph| { + let e = graph.edge(1, 2).expect("failed to get edge"); + let sum: u64 = e + .properties() + .temporal() + .get("tx_sent") + .unwrap() + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum(); - let e_btc = e.layers("btc").expect("failed to get btc layer"); - let e_eth = e.layers("eth").expect("failed to get eth layer"); + assert_eq!(sum, 100); - let edge_btc_sum = e_btc - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum::(); + let lg = graph + .layers(vec!["eth", "btc"]) + .expect("failed to layer graph"); - let edge_eth_sum = e_eth - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum::(); + let e = lg.edge(1, 2).expect("failed to get edge"); - assert!(edge_btc_sum < edge_eth_sum); + let sum_eth_btc: u64 = e + .properties() + .temporal() + .get("tx_sent") + .unwrap() + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum(); - let e_eth = e_eth - .layers(vec!["eth", "btc"]) - .expect("failed to get eth,btc layers"); + assert_eq!(sum_eth_btc, 30); - let eth_sum = e_eth - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum::(); + assert_eq!(lg.count_edges(), 1); - // layer does not have a way to reset yet! - assert_eq!(eth_sum, 20); - }); -} + let e = graph.edge(1, 2).expect("failed to get edge"); -#[test] -fn test_unique_layers() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + let e_btc = e.layers("btc").expect("failed to get btc layer"); + let e_eth = e.layers("eth").expect("failed to get eth layer"); - test_storage!(&graph, |graph| { - assert_eq!( - graph - .layers("layer2") + let edge_btc_sum = e_btc + .properties() + .temporal() + .get("tx_sent") .unwrap() - .unique_layers() - .collect_vec(), - vec!["layer2"] - ) - }); -} - -#[test] -fn node_from_id_is_consistent() { - proptest!(|(nodes: Vec)| { - let g = Graph::new(); - for v in nodes.iter() { - g.add_node(0, *v, NO_PROPS, None, None).unwrap(); - } - prop_assert!(g.nodes() - .name() - .into_iter_values() - .map(|name| g.node(name)) - .all(|v| v.is_some())); - }); -} + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum::(); -#[test] -fn large_id_is_consistent() { - global_info_logger(); - let g = Graph::new(); - g.add_node(0, 10000000000000000006, NO_PROPS, None, None) - .unwrap(); - info!("names: {:?}", g.nodes().name().collect_vec()); - assert!(g - .nodes() - .name() - .into_iter_values() - .map(|name| g.node(name)) - .all(|v| v.is_some())) -} + let edge_eth_sum = e_eth + .properties() + .temporal() + .get("tx_sent") + .unwrap() + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum::(); -#[test] -fn exploded_edge_times_is_consistent() { - let edges = proptest::collection::vec( - ( - 0u64..100, - 0u64..100, - proptest::collection::vec(-1000i64..1000i64, 1..40), - ), - 1..400, - ); - proptest!(|(edges in edges, offset in -1000i64..1000i64)| { - prop_assert!(check_exploded_edge_times_is_consistent(edges, offset)); - }); -} + assert!(edge_btc_sum < edge_eth_sum); -#[test] -fn exploded_edge_times_is_consistent_1() { - let edges = vec![(0, 0, vec![0, 1])]; - assert!(check_exploded_edge_times_is_consistent(edges, 0)); -} + let e_eth = e_eth + .layers(vec!["eth", "btc"]) + .expect("failed to get eth,btc layers"); -fn check_exploded_edge_times_is_consistent(edges: Vec<(u64, u64, Vec)>, offset: i64) -> bool { - global_info_logger(); - let mut correct = true; - let mut check = |condition: bool, message: String| { - if !condition { - error!("Failed: {}", message); - } - correct = correct && condition; - }; - // checks that exploded edges are preserved with correct timestamps - let mut edges: Vec<(GID, GID, Vec)> = edges - .into_iter() - .filter_map(|(src, dst, mut ts)| { - ts.sort(); - ts.dedup(); - let ts: Vec<_> = ts.into_iter().filter(|&t| t < i64::MAX).collect(); - (!ts.is_empty()).then_some((GID::U64(src), GID::U64(dst), ts)) - }) - .collect(); - edges.sort(); - edges.dedup_by_key(|(src, dst, _)| src.as_u64().zip(dst.as_u64())); + let eth_sum = e_eth + .properties() + .temporal() + .get("tx_sent") + .unwrap() + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum::(); - let g = Graph::new(); - for (src, dst, times) in edges.iter() { - for &t in times.iter() { - if t < i64::MAX { - g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - } + // layer does not have a way to reset yet! + assert_eq!(eth_sum, 20); + }); } - let mut actual_edges: Vec<(GID, GID, Vec)> = g - .edges() - .iter() - .map(|e| { - ( - e.src().id(), - e.dst().id(), - e.explode() - .iter() - .map(|ee| { - check( - ee.earliest_time() == ee.latest_time(), - format!("times mismatched for {:?}", ee), - ); // times are the same for exploded edge - let t = ee.earliest_time().unwrap().t(); - check( - ee.at(t).is_active(), - format!("exploded edge {:?} inactive at {}", ee, t), - ); - let t_test = t.saturating_add(offset); - if t_test != t && t_test < i64::MAX && t_test > i64::MIN { - check( - !ee.at(t_test).is_active(), - format!("exploded edge {:?} active at {}", ee, t_test), - ); - } - t - }) - .collect(), + #[test] + fn test_unique_layers() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + + test_storage!(&graph, |graph| { + assert_eq!( + graph + .layers("layer2") + .unwrap() + .unique_layers() + .collect_vec(), + vec!["layer2"] ) - }) - .collect(); + }); + } - for e in actual_edges.iter_mut() { - e.2.sort(); - } - actual_edges.sort(); - check( - actual_edges == edges, - format!( - "actual edges didn't match input actual: {:?}, expected: {:?}", - actual_edges, edges - ), - ); - correct -} + #[test] + fn node_from_id_is_consistent() { + proptest!(|(nodes: Vec)| { + let g = Graph::new(); + for v in nodes.iter() { + g.add_node(0, *v, NO_PROPS, None, None).unwrap(); + } + prop_assert!(g.nodes() + .name() + .into_iter_values() + .map(|name| g.node(name)) + .all(|v| v.is_some())); + }); + } -#[test] -fn can_apply_algorithm_on_filtered_graph() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, [("layer", 1)], Some("1")).unwrap(); - graph.add_edge(1, 1, 3, [("layer", 1)], Some("1")).unwrap(); - graph.add_edge(1, 2, 3, [("layer", 2)], Some("2")).unwrap(); - graph.add_edge(2, 3, 4, [("layer", 2)], Some("2")).unwrap(); - graph.add_edge(0, 1, 3, [("layer", 2)], Some("2")).unwrap(); - - test_storage!(&graph, |graph| { - let wl = graph.window(0, 3).layers(vec!["1", "2"]).unwrap(); - assert_eq!(weakly_connected_components(&wl).groups().len(), 1); - }); -} + #[test] + fn large_id_is_consistent() { + global_info_logger(); + let g = Graph::new(); + g.add_node(0, 10000000000000000006, NO_PROPS, None, None) + .unwrap(); + info!("names: {:?}", g.nodes().name().collect_vec()); + assert!(g + .nodes() + .name() + .into_iter_values() + .map(|name| g.node(name)) + .all(|v| v.is_some())) + } -#[test] -fn test_node_state_merge() { - let graph = Graph::new(); - for i in 0..1_000 { - graph.add_edge(0, i, i + 1, NO_PROPS, None).unwrap(); - } - - let sg = graph.subgraph(1..200); - let degs = degree_centrality(&graph); - let pr = unweighted_page_rank(&sg, None, None, None, false, None); - - let m1 = pr.state.merge( - °s.state, - MergePriority::Left, - MergePriority::Left, - Some(HashMap::from([( - "degree_centrality".to_string(), - MergePriority::Right, - )])), - ); - assert_eq!(m1.values().num_rows(), sg.count_nodes()); - - let m2 = degs.state.merge( - &pr.state, - MergePriority::Left, - MergePriority::Left, - Some(HashMap::from([( - "pagerank_score".to_string(), - MergePriority::Right, - )])), - ); - assert_eq!(m2.values().num_rows(), graph.count_nodes()); -} + #[test] + fn exploded_edge_times_is_consistent() { + let edges = proptest::collection::vec( + ( + 0u64..100, + 0u64..100, + proptest::collection::vec(-1000i64..1000i64, 1..40), + ), + 1..400, + ); + proptest!(|(edges in edges, offset in -1000i64..1000i64)| { + prop_assert!(check_exploded_edge_times_is_consistent(edges, offset)); + }); + } -#[test] -#[cfg(feature = "proto")] -fn save_load_serial() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - let dir = tempfile::tempdir().unwrap(); - let file_path = dir.path().join("abcd11"); - g.encode(&file_path).unwrap(); - let gg = Graph::decode(&file_path).unwrap(); - assert_graph_equal(&g, &gg); -} + #[test] + fn exploded_edge_times_is_consistent_1() { + let edges = vec![(0, 0, vec![0, 1])]; + assert!(check_exploded_edge_times_is_consistent(edges, 0)); + } -#[test] -fn test_node_type_changes() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, Some("typeA"), None).unwrap(); - g.add_node(1, "A", NO_PROPS, None, None).unwrap(); - let node_a = g.node("A").unwrap(); - assert_eq!(node_a.node_type().as_str(), Some("typeA")); - let result = g.add_node(2, "A", NO_PROPS, Some("typeB"), None); - assert!(result.is_err()); -} + fn check_exploded_edge_times_is_consistent( + edges: Vec<(u64, u64, Vec)>, + offset: i64, + ) -> bool { + global_info_logger(); + let mut correct = true; + let mut check = |condition: bool, message: String| { + if !condition { + error!("Failed: {}", message); + } + correct = correct && condition; + }; + // checks that exploded edges are preserved with correct timestamps + let mut edges: Vec<(GID, GID, Vec)> = edges + .into_iter() + .filter_map(|(src, dst, mut ts)| { + ts.sort(); + ts.dedup(); + let ts: Vec<_> = ts.into_iter().filter(|&t| t < i64::MAX).collect(); + (!ts.is_empty()).then_some((GID::U64(src), GID::U64(dst), ts)) + }) + .collect(); + edges.sort(); + edges.dedup_by_key(|(src, dst, _)| src.as_u64().zip(dst.as_u64())); -#[test] -fn test_layer_degree() { - let g = Graph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - g.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - g.add_edge(2, 1, 3, NO_PROPS, Some("layer1")).unwrap(); - g.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + let g = Graph::new(); + for (src, dst, times) in edges.iter() { + for &t in times.iter() { + if t < i64::MAX { + g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + } + } - test_storage!(&g, |g| { - let n = g.node(1).unwrap(); - let n_layer = n.layers("layer1").unwrap(); - assert_eq!(n_layer.out_degree(), 2); - assert_eq!(n_layer.in_degree(), 0); - assert_eq!(n_layer.degree(), 2); - }); -} + let mut actual_edges: Vec<(GID, GID, Vec)> = g + .edges() + .iter() + .map(|e| { + ( + e.src().id(), + e.dst().id(), + e.explode() + .iter() + .map(|ee| { + check( + ee.earliest_time() == ee.latest_time(), + format!("times mismatched for {:?}", ee), + ); // times are the same for exploded edge + let t = ee.earliest_time().unwrap().t(); + check( + ee.at(t).is_active(), + format!("exploded edge {:?} inactive at {}", ee, t), + ); + let t_test = t.saturating_add(offset); + if t_test != t && t_test < i64::MAX && t_test > i64::MIN { + check( + !ee.at(t_test).is_active(), + format!("exploded edge {:?} active at {}", ee, t_test), + ); + } + t + }) + .collect(), + ) + }) + .collect(); + + for e in actual_edges.iter_mut() { + e.2.sort(); + } + actual_edges.sort(); + check( + actual_edges == edges, + format!( + "actual edges didn't match input actual: {:?}, expected: {:?}", + actual_edges, edges + ), + ); + correct + } -#[test] -fn test_layer_name() { - let graph = Graph::new(); + #[test] + fn can_apply_algorithm_on_filtered_graph() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, [("layer", 1)], Some("1")).unwrap(); + graph.add_edge(1, 1, 3, [("layer", 1)], Some("1")).unwrap(); + graph.add_edge(1, 2, 3, [("layer", 2)], Some("2")).unwrap(); + graph.add_edge(2, 3, 4, [("layer", 2)], Some("2")).unwrap(); + graph.add_edge(0, 1, 3, [("layer", 2)], Some("2")).unwrap(); + + test_storage!(&graph, |graph| { + let wl = graph.window(0, 3).layers(vec!["1", "2"]).unwrap(); + assert_eq!(weakly_connected_components(&wl).groups().len(), 1); + }); + } - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph - .add_edge(0, 0, 2, NO_PROPS, Some("awesome layer")) - .unwrap(); + #[test] + fn test_node_state_merge() { + let graph = Graph::new(); + for i in 0..1_000 { + graph.add_edge(0, i, i + 1, NO_PROPS, None).unwrap(); + } - assert_eq!(graph.edge(0, 1).unwrap().layer_names(), ["_default"]); - assert_eq!(graph.edge(0, 2).unwrap().layer_names(), ["awesome layer"]); -} + let sg = graph.subgraph(1..200); + let degs = degree_centrality(&graph); + let pr = unweighted_page_rank(&sg, None, None, None, false, None); + + let m1 = pr.state.merge( + °s.state, + MergePriority::Left, + MergePriority::Left, + Some(HashMap::from([( + "degree_centrality".to_string(), + MergePriority::Right, + )])), + ); + assert_eq!(m1.values().num_rows(), sg.count_nodes()); + + let m2 = degs.state.merge( + &pr.state, + MergePriority::Left, + MergePriority::Left, + Some(HashMap::from([( + "pagerank_score".to_string(), + MergePriority::Right, + )])), + ); + assert_eq!(m2.values().num_rows(), graph.count_nodes()); + } -#[test] -fn test_type_filter() { - let g = PersistentGraph::new(); + #[test] + #[cfg(feature = "proto")] + fn save_load_serial() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + let dir = tempfile::tempdir().unwrap(); + let file_path = dir.path().join("abcd11"); + g.encode(&file_path).unwrap(); + let gg = Graph::decode(&file_path).unwrap(); + assert_graph_equal(&g, &gg); + } - g.add_node(1, 1, NO_PROPS, Some("wallet"), None).unwrap(); - g.add_node(1, 2, NO_PROPS, Some("timer"), None).unwrap(); - g.add_node(1, 3, NO_PROPS, Some("timer"), None).unwrap(); - g.add_node(1, 4, NO_PROPS, Some("wallet"), None).unwrap(); + #[test] + fn test_node_type_changes() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, Some("typeA"), None).unwrap(); + g.add_node(1, "A", NO_PROPS, None, None).unwrap(); + let node_a = g.node("A").unwrap(); + assert_eq!(node_a.node_type().as_str(), Some("typeA")); + let result = g.add_node(2, "A", NO_PROPS, Some("typeB"), None); + assert!(result.is_err()); + } - assert_eq!( - g.nodes() - .type_filter(["wallet"]) - .name() - .into_iter_values() - .sorted() - .collect_vec(), - vec!["1", "4"] - ); - - let g = Graph::new(); - g.add_node(1, 1, NO_PROPS, Some("a"), None).unwrap(); - g.add_node(1, 2, NO_PROPS, Some("b"), None).unwrap(); - g.add_node(1, 3, NO_PROPS, Some("b"), None).unwrap(); - g.add_node(1, 4, NO_PROPS, Some("a"), None).unwrap(); - g.add_node(1, 5, NO_PROPS, Some("c"), None).unwrap(); - g.add_node(1, 6, NO_PROPS, Some("e"), None).unwrap(); - g.add_node(1, 7, NO_PROPS, None, None).unwrap(); - g.add_node(1, 8, NO_PROPS, None, None).unwrap(); - g.add_node(1, 9, NO_PROPS, None, None).unwrap(); - g.add_edge(2, 1, 2, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 3, 2, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 2, 4, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 4, 5, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 4, 5, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 5, 6, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 3, 6, NO_PROPS, Some("a")).unwrap(); - - assert_eq!( - g.nodes() - .type_filter(["a", "b", "c", "e"]) - .name() - .sort_by_values(false), - vec!["1", "2", "3", "4", "5", "6"] - ); - - assert_eq!( - g.nodes().type_filter(Vec::::new()).name(), - Vec::::new() - ); - - assert_eq!( - g.nodes().type_filter([""]).name().sort_by_values(false), - vec!["7", "8", "9"] - ); - - let w = g.window(1, 4); - assert_eq!( - w.nodes().type_filter(["a"]).degree().sort_by_id(), - vec![1, 2] - ); - assert_eq!( - w.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c", "b"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - - let l = g.layers(["a"]).unwrap(); - assert_eq!( - l.nodes().type_filter(["a"]).degree().sort_by_id(), - vec![1, 2] - ); - assert_eq!( - l.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c", "b"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - - let sg = g.subgraph([1, 2, 3, 4, 5, 6]); - assert_eq!( - sg.nodes().type_filter(["a"]).degree().sort_by_id(), - vec![1, 2] - ); - assert_eq!( - sg.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c", "b"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect_vec()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - - assert_eq!( - g.nodes().degree().sort_by_id(), - vec![1, 3, 2, 2, 2, 2, 0, 0, 0] - ); - assert_eq!( - g.nodes().type_filter(["a"]).degree().sort_by_id(), - vec![1, 2] - ); - assert_eq!( - g.nodes().type_filter(["d"]).degree().sort_by_id(), - Vec::::new() - ); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .par_iter() - .map(|v| (v, v.degree())) - .collect::>() - .into_iter() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v) - .collect_vec(), - vec![1, 2] - ); - assert_eq!( - g.nodes() - .type_filter(["d"]) - .par_iter() - .map(|v| v.degree()) - .collect::>(), - Vec::::new() - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .collect() - .into_iter() - .map(|n| n.name()) - .collect_vec() - .into_iter() - .sorted() - .collect_vec(), - vec!["1", "4"] - ); - assert_eq!( - g.nodes() - .type_filter(Vec::<&str>::new()) - .collect() - .into_iter() - .map(|n| n.name()) - .collect_vec(), - Vec::<&str>::new() - ); + #[test] + fn test_layer_degree() { + let g = Graph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + g.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + g.add_edge(2, 1, 3, NO_PROPS, Some("layer1")).unwrap(); + g.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&g, |g| { + let n = g.node(1).unwrap(); + let n_layer = n.layers("layer1").unwrap(); + assert_eq!(n_layer.out_degree(), 2); + assert_eq!(n_layer.in_degree(), 0); + assert_eq!(n_layer.degree(), 2); + }); + } - assert_eq!(g.nodes().len(), 9); - assert_eq!(g.nodes().type_filter(["b"]).len(), 2); - assert_eq!(g.nodes().type_filter(["d"]).len(), 0); + #[test] + fn test_layer_name() { + let graph = Graph::new(); - assert!(!g.nodes().is_empty()); - assert!(g.nodes().type_filter(["d"]).is_empty()); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph + .add_edge(0, 0, 2, NO_PROPS, Some("awesome layer")) + .unwrap(); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .name() - .into_iter_values() - .collect_vec(), - vec!["1", "4"] - ); - assert_eq!( - g.nodes() - .type_filter(["a", "c"]) - .name() - .into_iter_values() - .sorted() - .collect_vec(), - vec!["1", "4", "5"] - ); + assert_eq!(graph.edge(0, 1).unwrap().layer_names(), ["_default"]); + assert_eq!(graph.edge(0, 2).unwrap().layer_names(), ["awesome layer"]); + } - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - assert_eq!( - g.nodes() - .type_filter(["a", "c"]) - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"], vec!["4", "6"]] - ); - assert_eq!( - g.nodes() - .type_filter(["d"]) - .neighbours() - .name() - .map(|(_, n)| n.collect_vec()) - .collect_vec(), - Vec::>::new() - ); + #[test] + fn test_type_filter() { + let g = PersistentGraph::new(); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.collect::>()) - .collect_vec(), - vec![vec![], vec!["5"]] - ); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(Vec::<&str>::new()) - .name() - .map(|(_, n)| { n.collect::>() }) - .collect_vec(), - vec![vec![], Vec::<&str>::new()] - ); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c", "b"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - assert_eq!( - g.nodes() + g.add_node(1, 1, NO_PROPS, Some("wallet"), None).unwrap(); + g.add_node(1, 2, NO_PROPS, Some("timer"), None).unwrap(); + g.add_node(1, 3, NO_PROPS, Some("timer"), None).unwrap(); + g.add_node(1, 4, NO_PROPS, Some("wallet"), None).unwrap(); + + assert_eq!( + g.nodes() + .type_filter(["wallet"]) + .name() + .into_iter_values() + .sorted() + .collect_vec(), + vec!["1", "4"] + ); + + let g = Graph::new(); + g.add_node(1, 1, NO_PROPS, Some("a"), None).unwrap(); + g.add_node(1, 2, NO_PROPS, Some("b"), None).unwrap(); + g.add_node(1, 3, NO_PROPS, Some("b"), None).unwrap(); + g.add_node(1, 4, NO_PROPS, Some("a"), None).unwrap(); + g.add_node(1, 5, NO_PROPS, Some("c"), None).unwrap(); + g.add_node(1, 6, NO_PROPS, Some("e"), None).unwrap(); + g.add_node(1, 7, NO_PROPS, None, None).unwrap(); + g.add_node(1, 8, NO_PROPS, None, None).unwrap(); + g.add_node(1, 9, NO_PROPS, None, None).unwrap(); + g.add_edge(2, 1, 2, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 3, 2, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 2, 4, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 4, 5, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 4, 5, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 5, 6, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 3, 6, NO_PROPS, Some("a")).unwrap(); + + assert_eq!( + g.nodes() + .type_filter(["a", "b", "c", "e"]) + .name() + .sort_by_values(false), + vec!["1", "2", "3", "4", "5", "6"] + ); + + assert_eq!( + g.nodes().type_filter(Vec::::new()).name(), + Vec::::new() + ); + + assert_eq!( + g.nodes().type_filter([""]).name().sort_by_values(false), + vec!["7", "8", "9"] + ); + + let w = g.window(1, 4); + assert_eq!( + w.nodes().type_filter(["a"]).degree().sort_by_id(), + vec![1, 2] + ); + assert_eq!( + w.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c", "b"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + + let l = g.layers(["a"]).unwrap(); + assert_eq!( + l.nodes().type_filter(["a"]).degree().sort_by_id(), + vec![1, 2] + ); + assert_eq!( + l.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c", "b"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + + let sg = g.subgraph([1, 2, 3, 4, 5, 6]); + assert_eq!( + sg.nodes().type_filter(["a"]).degree().sort_by_id(), + vec![1, 2] + ); + assert_eq!( + sg.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c", "b"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect_vec()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + + assert_eq!( + g.nodes().degree().sort_by_id(), + vec![1, 3, 2, 2, 2, 2, 0, 0, 0] + ); + assert_eq!( + g.nodes().type_filter(["a"]).degree().sort_by_id(), + vec![1, 2] + ); + assert_eq!( + g.nodes().type_filter(["d"]).degree().sort_by_id(), + Vec::::new() + ); + assert_eq!( + g.nodes() + .type_filter(["a"]) + .par_iter() + .map(|v| (v, v.degree())) + .collect::>() + .into_iter() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v) + .collect_vec(), + vec![1, 2] + ); + assert_eq!( + g.nodes() + .type_filter(["d"]) + .par_iter() + .map(|v| v.degree()) + .collect::>(), + Vec::::new() + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .collect() + .into_iter() + .map(|n| n.name()) + .collect_vec() + .into_iter() + .sorted() + .collect_vec(), + vec!["1", "4"] + ); + assert_eq!( + g.nodes() + .type_filter(Vec::<&str>::new()) + .collect() + .into_iter() + .map(|n| n.name()) + .collect_vec(), + Vec::<&str>::new() + ); + + assert_eq!(g.nodes().len(), 9); + assert_eq!(g.nodes().type_filter(["b"]).len(), 2); + assert_eq!(g.nodes().type_filter(["d"]).len(), 0); + + assert!(!g.nodes().is_empty()); + assert!(g.nodes().type_filter(["d"]).is_empty()); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .name() + .into_iter_values() + .collect_vec(), + vec!["1", "4"] + ); + assert_eq!( + g.nodes() + .type_filter(["a", "c"]) + .name() + .into_iter_values() + .sorted() + .collect_vec(), + vec!["1", "4", "5"] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + assert_eq!( + g.nodes() + .type_filter(["a", "c"]) + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"], vec!["4", "6"]] + ); + assert_eq!( + g.nodes() + .type_filter(["d"]) + .neighbours() + .name() + .map(|(_, n)| n.collect_vec()) + .collect_vec(), + Vec::>::new() + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.collect::>()) + .collect_vec(), + vec![vec![], vec!["5"]] + ); + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(Vec::<&str>::new()) + .name() + .map(|(_, n)| { n.collect::>() }) + .collect_vec(), + vec![vec![], Vec::<&str>::new()] + ); + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c", "b"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["d"]) + .name() + .map(|(_, n)| { n.collect::>() }) + .collect_vec(), + vec![vec![], Vec::<&str>::new()] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["1", "3", "4"], vec!["1", "3", "4", "4", "6"]] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c"]) + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec![], vec!["4", "6"]] + ); + + assert_eq!( + g.nodes() + .neighbours() + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![ + vec!["1", "3", "4"], + vec!["2", "2", "2", "5", "6"], + vec!["1", "3", "3", "4", "5"], + vec!["1", "3", "4", "4", "6"], + vec!["2", "3", "5", "5"], + vec!["2", "4", "6", "6"], + vec![], + vec![], + vec![], + ] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["d"]) + .total_count(), + 0 + ); + + assert!(g + .nodes() .type_filter(["a"]) .neighbours() .type_filter(["d"]) - .name() - .map(|(_, n)| { n.collect::>() }) - .collect_vec(), - vec![vec![], Vec::<&str>::new()] - ); + .is_all_empty()); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["1", "3", "4"], vec!["1", "3", "4", "4", "6"]] - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c"]) - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec![], vec!["4", "6"]] - ); - - assert_eq!( - g.nodes() - .neighbours() - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![ - vec!["1", "3", "4"], + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["d"]) + .iter() + .map(|(_, n)| { n.name().collect::>() }) + .collect_vec(), + vec![vec![], Vec::<&str>::new()] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["b"]) + .collect() + .into_iter() + .flatten() + .map(|n| n.name()) + .collect_vec(), + vec!["2", "2"] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["d"]) + .collect() + .into_iter() + .flatten() + .map(|n| n.name()) + .collect_vec(), + Vec::<&str>::new() + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .name() + .sorted() + .collect_vec(), + vec!["1", "3", "4"] + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["b"]) + .name() + .collect_vec(), + vec!["3"] + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["d"]) + .name() + .collect_vec(), + Vec::<&str>::new() + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["c", "a"]) + .name() + .sorted() + .collect_vec(), + vec!["1", "4"] + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["c"]) + .neighbours() + .name() + .collect_vec(), + Vec::<&str>::new() + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .neighbours() + .name() + .sorted() + .collect_vec(), vec!["2", "2", "2", "5", "6"], - vec!["1", "3", "3", "4", "5"], - vec!["1", "3", "4", "4", "6"], - vec!["2", "3", "5", "5"], - vec!["2", "4", "6", "6"], - vec![], - vec![], - vec![], - ] - ); + ); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["d"]) - .total_count(), - 0 - ); - - assert!(g - .nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["d"]) - .is_all_empty()); - - assert_eq!( - g.nodes() - .type_filter(["a"]) + assert_eq!( + g.node("2").unwrap().neighbours().type_filter(["d"]).len(), + 0 + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["a"]) + .neighbours() + .len(), + 3 + ); + + assert!(g + .node("2") + .unwrap() .neighbours() .type_filter(["d"]) - .iter() - .map(|(_, n)| { n.name().collect::>() }) - .collect_vec(), - vec![vec![], Vec::<&str>::new()] - ); + .is_empty()); - assert_eq!( - g.nodes() - .type_filter(["a"]) + assert!(!g + .node("2") + .unwrap() .neighbours() - .type_filter(["b"]) - .collect() - .into_iter() - .flatten() - .map(|n| n.name()) - .collect_vec(), - vec!["2", "2"] - ); - - assert_eq!( - g.nodes() .type_filter(["a"]) .neighbours() - .type_filter(["d"]) - .collect() - .into_iter() - .flatten() - .map(|n| n.name()) - .collect_vec(), - Vec::<&str>::new() - ); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .name() - .sorted() - .collect_vec(), - vec!["1", "3", "4"] - ); + .is_empty()); - assert_eq!( - g.node("2") + assert!(g + .node("2") .unwrap() .neighbours() - .type_filter(["b"]) - .name() - .collect_vec(), - vec!["3"] - ); + .type_filter(["d"]) + .neighbours() + .is_empty()); - assert_eq!( - g.node("2") + assert!(g + .node("2") .unwrap() .neighbours() .type_filter(["d"]) - .name() - .collect_vec(), - Vec::<&str>::new() - ); + .iter() + .collect_vec() + .is_empty(),); - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .type_filter(["c", "a"]) - .name() - .sorted() - .collect_vec(), - vec!["1", "4"] - ); + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["b"]) + .collect() + .into_iter() + .map(|n| n.name()) + .collect_vec(), + vec!["3"] + ); - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .type_filter(["c"]) - .neighbours() - .name() - .collect_vec(), - Vec::<&str>::new() - ); + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["d"]) + .collect() + .into_iter() + .map(|n| n.name()) + .collect_vec(), + Vec::<&str>::new() + ); + } + + #[test] + fn test_persistent_graph() { + let g = Graph::new(); + g.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) + .unwrap(); + assert_eq!( + g.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + + let pg = g.persistent_graph(); + pg.delete_edge(10, 0, 1, None).unwrap(); + assert_eq!( + g.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + } - assert_eq!( - g.node("2") + #[test] + fn test_unique_property() { + let g = Graph::new(); + g.add_edge(1, 1, 2, [("status", "open")], None).unwrap(); + g.add_edge(2, 1, 2, [("status", "open")], None).unwrap(); + g.add_edge(3, 1, 2, [("status", "review")], None).unwrap(); + g.add_edge(4, 1, 2, [("status", "open")], None).unwrap(); + g.add_edge(5, 1, 2, [("status", "in-progress")], None) + .unwrap(); + g.add_edge(10, 1, 2, [("status", "in-progress")], None) + .unwrap(); + g.add_edge(9, 1, 2, [("state", true)], None).unwrap(); + g.add_edge(10, 1, 2, [("state", false)], None).unwrap(); + g.add_edge(6, 1, 2, NO_PROPS, None).unwrap(); + + let mut props = g + .edge(1, 2) .unwrap() - .neighbours() - .neighbours() - .name() - .sorted() - .collect_vec(), - vec!["2", "2", "2", "5", "6"], - ); - - assert_eq!( - g.node("2").unwrap().neighbours().type_filter(["d"]).len(), - 0 - ); - - assert_eq!( - g.node("2") + .properties() + .temporal() + .get("status") .unwrap() - .neighbours() - .type_filter(["a"]) - .neighbours() - .len(), - 3 - ); - - assert!(g - .node("2") - .unwrap() - .neighbours() - .type_filter(["d"]) - .is_empty()); - - assert!(!g - .node("2") - .unwrap() - .neighbours() - .type_filter(["a"]) - .neighbours() - .is_empty()); - - assert!(g - .node("2") - .unwrap() - .neighbours() - .type_filter(["d"]) - .neighbours() - .is_empty()); - - assert!(g - .node("2") - .unwrap() - .neighbours() - .type_filter(["d"]) - .iter() - .collect_vec() - .is_empty(),); - - assert_eq!( - g.node("2") + .unique() + .into_iter() + .map(|x| x.unwrap_str().to_string()) + .collect_vec(); + props.sort(); + assert_eq!(props, vec!["in-progress", "open", "review"]); + + let ordered_dedupe_latest = g + .edge(1, 2) .unwrap() - .neighbours() - .type_filter(["b"]) - .collect() + .properties() + .temporal() + .get("status") + .unwrap() + .ordered_dedupe(true) .into_iter() - .map(|n| n.name()) - .collect_vec(), - vec!["3"] - ); + .map(|(x, y)| (x.t(), y.unwrap_str().to_string())) + .collect_vec(); - assert_eq!( - g.node("2") + assert_eq!( + ordered_dedupe_latest, + vec![ + (2, "open".to_string()), + (3, "review".to_string()), + (4, "open".to_string()), + (10, "in-progress".to_string()), + ] + ); + + let ordered_dedupe_earliest = g + .edge(1, 2) .unwrap() - .neighbours() - .type_filter(["d"]) - .collect() + .properties() + .temporal() + .get("status") + .unwrap() + .ordered_dedupe(false) .into_iter() - .map(|n| n.name()) - .collect_vec(), - Vec::<&str>::new() - ); -} + .map(|(x, y)| (x.t(), y.unwrap_str().to_string())) + .collect_vec(); -#[test] -fn test_persistent_graph() { - let g = Graph::new(); - g.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) - .unwrap(); - assert_eq!( - g.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - - let pg = g.persistent_graph(); - pg.delete_edge(10, 0, 1, None).unwrap(); - assert_eq!( - g.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); -} + assert_eq!( + ordered_dedupe_earliest, + vec![ + (1, "open".to_string()), + (3, "review".to_string()), + (4, "open".to_string()), + (5, "in-progress".to_string()), + ] + ); + } -#[test] -fn test_unique_property() { - let g = Graph::new(); - g.add_edge(1, 1, 2, [("status", "open")], None).unwrap(); - g.add_edge(2, 1, 2, [("status", "open")], None).unwrap(); - g.add_edge(3, 1, 2, [("status", "review")], None).unwrap(); - g.add_edge(4, 1, 2, [("status", "open")], None).unwrap(); - g.add_edge(5, 1, 2, [("status", "in-progress")], None) - .unwrap(); - g.add_edge(10, 1, 2, [("status", "in-progress")], None) - .unwrap(); - g.add_edge(9, 1, 2, [("state", true)], None).unwrap(); - g.add_edge(10, 1, 2, [("state", false)], None).unwrap(); - g.add_edge(6, 1, 2, NO_PROPS, None).unwrap(); - - let mut props = g - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("status") - .unwrap() - .unique() - .into_iter() - .map(|x| x.unwrap_str().to_string()) - .collect_vec(); - props.sort(); - assert_eq!(props, vec!["in-progress", "open", "review"]); - - let ordered_dedupe_latest = g - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("status") - .unwrap() - .ordered_dedupe(true) - .into_iter() - .map(|(x, y)| (x.t(), y.unwrap_str().to_string())) - .collect_vec(); - - assert_eq!( - ordered_dedupe_latest, - vec![ - (2, "open".to_string()), - (3, "review".to_string()), - (4, "open".to_string()), - (10, "in-progress".to_string()), - ] - ); - - let ordered_dedupe_earliest = g - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("status") - .unwrap() - .ordered_dedupe(false) - .into_iter() - .map(|(x, y)| (x.t(), y.unwrap_str().to_string())) - .collect_vec(); - - assert_eq!( - ordered_dedupe_earliest, - vec![ - (1, "open".to_string()), - (3, "review".to_string()), - (4, "open".to_string()), - (5, "in-progress".to_string()), - ] - ); -} + #[test] + fn test_create_node() { + let g = Graph::new(); + g.create_node(0, 1, [("test", Prop::Bool(true))], None, None) + .unwrap(); -#[test] -fn test_create_node() { - let g = Graph::new(); - g.create_node(0, 1, [("test", Prop::Bool(true))], None, None) - .unwrap(); + let n = g.node(1).unwrap(); - let n = g.node(1).unwrap(); + assert_eq!(n.id().as_u64().unwrap(), 1); + assert_eq!(n.properties().get("test").unwrap(), Prop::Bool(true)); - assert_eq!(n.id().as_u64().unwrap(), 1); - assert_eq!(n.properties().get("test").unwrap(), Prop::Bool(true)); + let result = g.create_node(1, 1, [("test".to_string(), Prop::Bool(true))], None, None); + assert!(matches!(result, Err(GraphError::NodeExistsError(id)) if id == GID::U64(1))); + } - let result = g.create_node(1, 1, [("test".to_string(), Prop::Bool(true))], None, None); - assert!(matches!(result, Err(GraphError::NodeExistsError(id)) if id == GID::U64(1))); -} + #[test] + fn test_materialize_edge_metadata() { + let g = Graph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.add_metadata([("test", "test")], None).unwrap(); + g.add_edge(10, 1, 2, NO_PROPS, Some("a")).unwrap(); -#[test] -fn test_materialize_edge_metadata() { - let g = Graph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.add_metadata([("test", "test")], None).unwrap(); - g.add_edge(10, 1, 2, NO_PROPS, Some("a")).unwrap(); - - let gw = g.after(1); - let gmw = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gmw); -} + let gw = g.after(1); + let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); + } -#[test] -fn test_id_filter() { - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + #[test] + fn test_id_filter() { + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - assert_eq!(graph.nodes().id(), [0, 1]); - assert_eq!(graph.nodes().id_filter([0]).len(), 1); - assert_eq!(graph.nodes().id_filter([0]).id(), [0]); - assert_eq!(graph.nodes().id_filter([0]).degree(), [1]); -} + assert_eq!(graph.nodes().id(), [0, 1]); + assert_eq!(graph.nodes().id_filter([0]).len(), 1); + assert_eq!(graph.nodes().id_filter([0]).id(), [0]); + assert_eq!(graph.nodes().id_filter([0]).degree(), [1]); + } -#[test] -fn test_indexed() { - proptest!(|(graph in build_graph_strat(10, 10, 10, 10, false), nodes in subsequence((0..10).collect::>(), 0..10))| { - let graph = Graph::from(build_graph(&graph)); - let expected_node_ids = nodes.iter().copied().filter(|&id| graph.has_node(id)).collect::>(); - let nodes = graph.nodes().id_filter(nodes); - assert_eq!(nodes.id(), expected_node_ids); - }) -} + #[test] + fn test_indexed() { + proptest!(|(graph in build_graph_strat(10, 10, 10, 10, false), nodes in subsequence((0..10).collect::>(), 0..10))| { + let graph = Graph::from(build_graph(&graph)); + let expected_node_ids = nodes.iter().copied().filter(|&id| graph.has_node(id)).collect::>(); + let nodes = graph.nodes().id_filter(nodes); + assert_eq!(nodes.id(), expected_node_ids); + }) + } -#[test] -fn materialize_window_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = Graph::from(build_graph(&graph_f)); - let gw = g.window(w.start, w.end); + #[test] + fn materialize_window_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = Graph::from(build_graph(&graph_f)); + let gw = g.window(w.start, w.end); + let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); + }) + } + + #[test] + fn materialize_temporal_properties_one_edge() { + let g = Graph::new(); + g.add_edge( + 0, + 0, + 0, + [("3", Prop::I64(1)), ("0", Prop::str("baa"))], + Some("a"), + ) + .unwrap(); + + let gw = g.window(-9, 3); let gmw = gw.materialize().unwrap(); assert_graph_equal(&gw, &gmw); - }) -} + } -#[test] -fn materialize_temporal_properties_one_edge() { - let g = Graph::new(); - g.add_edge( - 0, - 0, - 0, - [("3", Prop::I64(1)), ("0", Prop::str("baa"))], - Some("a"), - ) - .unwrap(); - - let gw = g.window(-9, 3); - let gmw = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gmw); -} + #[test] + fn materialize_one_node() { + let g = Graph::new(); + g.add_node(0, 0, NO_PROPS, None, None).unwrap(); -#[test] -fn materialize_one_node() { - let g = Graph::new(); - g.add_node(0, 0, NO_PROPS, None, None).unwrap(); + let n = g.node(0).unwrap(); + let hist = n.history(); + assert!(!hist.is_empty()); + let rows = n.rows().collect::>(); + assert!(!rows.is_empty()); - let n = g.node(0).unwrap(); - let hist = n.history(); - assert!(!hist.is_empty()); - let rows = n.rows().collect::>(); - assert!(!rows.is_empty()); + let gw = g.window(0, 1); + let gmw = gw.materialize().unwrap(); - let gw = g.window(0, 1); - let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); + } - assert_graph_equal(&gw, &gmw); -} + #[test] + fn materialize_some_edges() -> Result<(), GraphError> { + let edges1_props = EdgeUpdatesFixture { + props: PropUpdatesFixture { + t_props: vec![ + (2433054617899119663, vec![]), + ( + 5623371002478468619, + vec![("0".to_owned(), Prop::I64(-180204069376666762))], + ), + ], + c_props: vec![], + }, + deletions: vec![-3684372592923241629, 3668280323305195349], + }; -#[test] -fn materialize_some_edges() -> Result<(), GraphError> { - let edges1_props = EdgeUpdatesFixture { - props: PropUpdatesFixture { - t_props: vec![ - (2433054617899119663, vec![]), - ( - 5623371002478468619, - vec![("0".to_owned(), Prop::I64(-180204069376666762))], - ), + let edges2_props = EdgeUpdatesFixture { + props: PropUpdatesFixture { + t_props: vec![ + ( + -7888823724540213280, + vec![("0".to_owned(), Prop::I64(1339447446033500001))], + ), + (-3792330935693192039, vec![]), + ( + 4049942931077033460, + vec![("0".to_owned(), Prop::I64(-544773539725842277))], + ), + (5085404190610173488, vec![]), + (1445770503123270290, vec![]), + (-5628624083683143619, vec![]), + (-394401628579820652, vec![]), + (-2398199704888544233, vec![]), + ], + c_props: vec![("0".to_owned(), Prop::I64(-1877019573933389749))], + }, + deletions: vec![ + 3969804007878301015, + 7040207277685112004, + 7380699292468575143, + 3332576590029503186, + -1107894292705275349, + 6647229517972286485, + 6359226207899406831, ], - c_props: vec![], - }, - deletions: vec![-3684372592923241629, 3668280323305195349], - }; + }; - let edges2_props = EdgeUpdatesFixture { - props: PropUpdatesFixture { - t_props: vec![ - ( - -7888823724540213280, - vec![("0".to_owned(), Prop::I64(1339447446033500001))], - ), - (-3792330935693192039, vec![]), - ( - 4049942931077033460, - vec![("0".to_owned(), Prop::I64(-544773539725842277))], - ), - (5085404190610173488, vec![]), - (1445770503123270290, vec![]), - (-5628624083683143619, vec![]), - (-394401628579820652, vec![]), - (-2398199704888544233, vec![]), - ], - c_props: vec![("0".to_owned(), Prop::I64(-1877019573933389749))], - }, - deletions: vec![ - 3969804007878301015, - 7040207277685112004, - 7380699292468575143, - 3332576590029503186, - -1107894292705275349, - 6647229517972286485, - 6359226207899406831, - ], - }; + let edges: EdgeFixture = [ + ((2, 7, Some("b")), edges1_props), + ((7, 2, Some("a")), edges2_props), + ] + .into_iter() + .collect(); - let edges: EdgeFixture = [ - ((2, 7, Some("b")), edges1_props), - ((7, 2, Some("a")), edges2_props), - ] - .into_iter() - .collect(); - - let w = -3619743214445905380..90323088878877991; - let graph_f = GraphFixture { - nodes: NodeFixture::default(), - edges, - }; + let w = -3619743214445905380..90323088878877991; + let graph_f = GraphFixture { + nodes: NodeFixture::default(), + edges, + }; - let g = Graph::from(build_graph(&graph_f)); - let gw = g.window(w.start, w.end); - let gmw = gw.materialize()?; - assert_graph_equal(&gw, &gmw); + let g = Graph::from(build_graph(&graph_f)); + let gw = g.window(w.start, w.end); + let gmw = gw.materialize()?; + assert_graph_equal(&gw, &gmw); - Ok(()) -} + Ok(()) + } -#[test] -fn materialize_window_delete_test() { - let g = Graph::new(); - g.delete_edge(0, 0, 0, Some("a")).unwrap(); - let w = 0..1; - let gw = g.window(w.start, w.end); - let gmw = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gmw); -} + #[test] + fn materialize_window_delete_test() { + let g = Graph::new(); + g.delete_edge(0, 0, 0, Some("a")).unwrap(); + let w = 0..1; + let gw = g.window(w.start, w.end); + let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); + } -#[test] -fn test_multilayer() { - let g = Graph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(1, 0, 0, NO_PROPS, Some("a")).unwrap(); - let gw = g.window(0, 1); - - let expected = Graph::new(); - expected.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - expected.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gw, &expected); -} + #[test] + fn test_multilayer() { + let g = Graph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(1, 0, 0, NO_PROPS, Some("a")).unwrap(); + let gw = g.window(0, 1); + + let expected = Graph::new(); + expected.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + expected.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gw, &expected); + } -#[test] -fn test_empty_window() { - let g = Graph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - let gw = g.window(-1, 0); + #[test] + fn test_empty_window() { + let g = Graph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + let gw = g.window(-1, 0); + + assert!(g.window(-1, 0).nodes().is_empty()); + assert_eq!(g.window(-1, 0).count_nodes(), 0); + for layer in gw.unique_layers() { + let layered = gw.valid_layers(layer); + assert_eq!(layered.count_nodes(), 0); + } + } - assert!(g.window(-1, 0).nodes().is_empty()); - assert_eq!(g.window(-1, 0).count_nodes(), 0); - for layer in gw.unique_layers() { - let layered = gw.valid_layers(layer); - assert_eq!(layered.count_nodes(), 0); + #[test] + fn add_edge_and_read_props_concurrent() { + for t in 0..1000 { + let g = Graph::new(); + join( + || g.add_edge(t, 1, 2, [("test", true)], None).unwrap(), + || { + // if the edge exists already, it should have the property set + g.window(t, t + 1) + .edge(1, 2) + .map(|e| assert!(e.properties().get("test").is_some())) + }, + ); + } } -} -#[test] -fn add_edge_and_read_props_concurrent() { - for t in 0..1000 { + #[test] + fn test_group_by() { let g = Graph::new(); - join( - || g.add_edge(t, 1, 2, [("test", true)], None).unwrap(), - || { - // if the edge exists already, it should have the property set - g.window(t, t + 1) - .edge(1, 2) - .map(|e| assert!(e.properties().get("test").is_some())) - }, - ); - } -} + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); + g.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); + let groups_from_lazy = g.nodes().out_degree().groups(); + + let groups_from_eager = g.nodes().out_degree().compute().groups(); -#[test] -fn test_group_by() { - let g = Graph::new(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); - g.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); - let groups_from_lazy = g.nodes().out_degree().groups(); + let expected_groups: HashMap> = HashMap::from([ + (0, Arc::from_iter([GID::U64(3), GID::U64(5)])), + (1, Arc::from_iter([GID::U64(1), GID::U64(2), GID::U64(4)])), + ]); - let groups_from_eager = g.nodes().out_degree().compute().groups(); + let expected_subgraphs: HashMap> = HashMap::from([ + (0, Arc::from_iter([])), + (1, Arc::from_iter([GID::U64(1), GID::U64(2)])), + ]); - let expected_groups: HashMap> = HashMap::from([ - (0, Arc::from_iter([GID::U64(3), GID::U64(5)])), - (1, Arc::from_iter([GID::U64(1), GID::U64(2), GID::U64(4)])), - ]); + assert_eq!( + groups_from_lazy + .iter() + .map(|(v, nodes)| (*v, nodes.id().sort_by_values(false).values().clone())) + .collect::>(), + expected_groups + ); - let expected_subgraphs: HashMap> = HashMap::from([ - (0, Arc::from_iter([])), - (1, Arc::from_iter([GID::U64(1), GID::U64(2)])), - ]); + assert_eq!( + groups_from_lazy + .clone() + .into_iter_groups() + .map(|(v, nodes)| (v, nodes.id().sort_by_values(false).values().clone())) + .collect::>(), + expected_groups + ); - assert_eq!( - groups_from_lazy - .iter() - .map(|(v, nodes)| (*v, nodes.id().sort_by_values(false).values().clone())) - .collect::>(), - expected_groups - ); - - assert_eq!( - groups_from_lazy - .clone() - .into_iter_groups() - .map(|(v, nodes)| (v, nodes.id().sort_by_values(false).values().clone())) - .collect::>(), - expected_groups - ); - - assert_eq!( - groups_from_lazy - .iter_subgraphs() - .map(|(v, graph)| ( - *v, - graph.nodes().id().sort_by_values(false).values().clone() - )) - .collect::>(), - expected_subgraphs - ); - - assert_eq!( - groups_from_lazy - .clone() - .into_iter_subgraphs() - .map(|(v, graph)| (v, graph.nodes().id().sort_by_values(false).values().clone())) - .collect::>(), - expected_subgraphs - ); - - assert_eq!( - groups_from_eager - .iter() - .map(|(v, nodes)| (*v, nodes.id().sort_by_values(false).values().clone())) - .collect::>(), - expected_groups - ); + assert_eq!( + groups_from_lazy + .iter_subgraphs() + .map(|(v, graph)| ( + *v, + graph.nodes().id().sort_by_values(false).values().clone() + )) + .collect::>(), + expected_subgraphs + ); - assert_eq!(groups_from_lazy.len(), expected_groups.len()); + assert_eq!( + groups_from_lazy + .clone() + .into_iter_subgraphs() + .map(|(v, graph)| (v, graph.nodes().id().sort_by_values(false).values().clone())) + .collect::>(), + expected_subgraphs + ); - for (i, (v, nodes)) in groups_from_eager.iter().enumerate() { - let (v2, nodes2) = groups_from_eager.group(i).unwrap(); - assert_eq!(v, v2); - assert!(nodes.iter().eq(nodes2.iter())); - let (v3, graph) = groups_from_eager.group_subgraph(i).unwrap(); - assert_eq!(v, v3); assert_eq!( - graph.nodes().id().sort_by_values(false), - expected_subgraphs[v].deref() + groups_from_eager + .iter() + .map(|(v, nodes)| (*v, nodes.id().sort_by_values(false).values().clone())) + .collect::>(), + expected_groups ); + + assert_eq!(groups_from_lazy.len(), expected_groups.len()); + + for (i, (v, nodes)) in groups_from_eager.iter().enumerate() { + let (v2, nodes2) = groups_from_eager.group(i).unwrap(); + assert_eq!(v, v2); + assert!(nodes.iter().eq(nodes2.iter())); + let (v3, graph) = groups_from_eager.group_subgraph(i).unwrap(); + assert_eq!(v, v3); + assert_eq!( + graph.nodes().id().sort_by_values(false), + expected_subgraphs[v].deref() + ); + } } } diff --git a/raphtory/tests/df_loaders.rs b/raphtory/tests/df_loaders.rs index 970399c7c7..dff88b95d3 100644 --- a/raphtory/tests/df_loaders.rs +++ b/raphtory/tests/df_loaders.rs @@ -1,4 +1,4 @@ -#[cfg(feature = "io")] +#[cfg(all(test, feature = "test-utils", feature = "io"))] mod io_tests { use arrow::array::builder::{ ArrayBuilder, Int64Builder, LargeStringBuilder, StringViewBuilder, UInt64Builder, @@ -776,8 +776,7 @@ mod io_tests { } } -#[cfg(test)] -#[cfg(feature = "io")] +#[cfg(all(test, feature = "test-utils", feature = "io"))] mod parquet_tests { use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; diff --git a/raphtory/tests/edge_property_filter.rs b/raphtory/tests/edge_property_filter.rs index 06bb051190..e2497332f2 100644 --- a/raphtory/tests/edge_property_filter.rs +++ b/raphtory/tests/edge_property_filter.rs @@ -1,379 +1,384 @@ -use itertools::Itertools; -use proptest::{arbitrary::any, proptest}; -use raphtory::{ - db::{ - api::view::Filter, - graph::{ - assertions::{assert_ok_or_missing_edges, EdgeRow}, - graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, - views::{ - deletion_graph::PersistentGraph, - filter::model::{ - node_filter::ops::NodeFilterOps, property_filter::ops::PropertyFilterOps, - ComposableFilter, EdgeFilter, PropertyFilterFactory, +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use proptest::{arbitrary::any, proptest}; + use raphtory::{ + db::{ + api::view::Filter, + graph::{ + assertions::{assert_ok_or_missing_edges, EdgeRow}, + graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, + views::{ + deletion_graph::PersistentGraph, + filter::model::{ + node_filter::ops::NodeFilterOps, property_filter::ops::PropertyFilterOps, + ComposableFilter, EdgeFilter, PropertyFilterFactory, + }, }, }, }, - }, - prelude::*, - test_utils::{build_edge_deletions, build_edge_list, build_graph_from_edge_list, build_window}, -}; -use raphtory_api::core::{entities::properties::prop::PropType, storage::timeindex::AsTime}; -use raphtory_storage::mutation::addition_ops::{InternalAdditionOps, SessionAdditionOps}; - -#[test] -fn test_edge_filter() { - let g = Graph::new(); - g.add_edge(0, "Jimi", "John", [("band", "JH Experience")], None) - .unwrap(); - g.add_edge(1, "John", "David", [("band", "Dead & Company")], None) - .unwrap(); - g.add_edge(2, "David", "Jimi", [("band", "Pink Floyd")], None) - .unwrap(); - - let filter_expr = EdgeFilter::dst() - .name() - .eq("David") - .and(EdgeFilter.property("band").eq("Dead & Company")); - let filtered_edges = g.filter(filter_expr).unwrap(); - - assert_eq!( - filtered_edges - .edges() - .iter() - .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) - .collect::>(), - vec!["John->David"] - ); - - let g_expected = Graph::new(); - g_expected - .add_edge((1, 1), "John", "David", [("band", "Dead & Company")], None) - .unwrap(); - - assert_graph_equal(&filtered_edges, &g_expected); -} + prelude::*, + test_utils::{ + build_edge_deletions, build_edge_list, build_graph_from_edge_list, build_window, + }, + }; + use raphtory_api::core::{entities::properties::prop::PropType, storage::timeindex::AsTime}; + use raphtory_storage::mutation::addition_ops::{InternalAdditionOps, SessionAdditionOps}; + + #[test] + fn test_edge_filter() { + let g = Graph::new(); + g.add_edge(0, "Jimi", "John", [("band", "JH Experience")], None) + .unwrap(); + g.add_edge(1, "John", "David", [("band", "Dead & Company")], None) + .unwrap(); + g.add_edge(2, "David", "Jimi", [("band", "Pink Floyd")], None) + .unwrap(); + + let filter_expr = EdgeFilter::dst() + .name() + .eq("David") + .and(EdgeFilter.property("band").eq("Dead & Company")); + let filtered_edges = g.filter(filter_expr).unwrap(); + + assert_eq!( + filtered_edges + .edges() + .iter() + .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) + .collect::>(), + vec!["John->David"] + ); + + let g_expected = Graph::new(); + g_expected + .add_edge((1, 1), "John", "David", [("band", "Dead & Company")], None) + .unwrap(); + + assert_graph_equal(&filtered_edges, &g_expected); + } -#[test] -fn test_edge_filter_persistent() { - let g = PersistentGraph::new(); - g.add_edge(0, "Jimi", "John", [("band", "JH Experience")], None) - .unwrap(); - g.add_edge(1, "John", "David", [("band", "Dead & Company")], None) - .unwrap(); - g.add_edge(2, "David", "Jimi", [("band", "Pink Floyd")], None) - .unwrap(); - - let filter_expr = EdgeFilter::dst() - .name() - .eq("David") - .and(EdgeFilter.property("band").eq("Dead & Company")); - let filtered_edges = g.filter(filter_expr).unwrap(); - - let g_expected = PersistentGraph::new(); - g_expected - .add_edge((1, 1), "John", "David", [("band", "Dead & Company")], None) - .unwrap(); - - assert_eq!( - filtered_edges - .edges() - .iter() - .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) - .collect::>(), - vec!["John->David"] - ); - assert_graph_equal(&filtered_edges, &g_expected); -} + #[test] + fn test_edge_filter_persistent() { + let g = PersistentGraph::new(); + g.add_edge(0, "Jimi", "John", [("band", "JH Experience")], None) + .unwrap(); + g.add_edge(1, "John", "David", [("band", "Dead & Company")], None) + .unwrap(); + g.add_edge(2, "David", "Jimi", [("band", "Pink Floyd")], None) + .unwrap(); + + let filter_expr = EdgeFilter::dst() + .name() + .eq("David") + .and(EdgeFilter.property("band").eq("Dead & Company")); + let filtered_edges = g.filter(filter_expr).unwrap(); + + let g_expected = PersistentGraph::new(); + g_expected + .add_edge((1, 1), "John", "David", [("band", "Dead & Company")], None) + .unwrap(); + + assert_eq!( + filtered_edges + .edges() + .iter() + .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) + .collect::>(), + vec!["John->David"] + ); + assert_graph_equal(&filtered_edges, &g_expected); + } -#[test] -fn test_edge_property_filter_on_nodes() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("test", 1i64)], None).unwrap(); - g.add_edge(0, 1, 3, [("test", 3i64)], None).unwrap(); - g.add_edge(1, 2, 3, [("test", 2i64)], None).unwrap(); - g.add_edge(1, 2, 4, [("test", 0i64)], None).unwrap(); - - let filter = EdgeFilter.property("test").eq(1i64); - let n1 = g.node(1).unwrap().filter(filter).unwrap(); - assert_eq!( - n1.edges().id().collect_vec(), - vec![(GID::U64(1), GID::U64(2))] - ); - let n2 = g - .node(2) - .unwrap() - .filter(EdgeFilter.property("test").gt(1i64)) - .unwrap(); - assert_eq!( - n2.edges().id().collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); -} + #[test] + fn test_edge_property_filter_on_nodes() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("test", 1i64)], None).unwrap(); + g.add_edge(0, 1, 3, [("test", 3i64)], None).unwrap(); + g.add_edge(1, 2, 3, [("test", 2i64)], None).unwrap(); + g.add_edge(1, 2, 4, [("test", 0i64)], None).unwrap(); + + let filter = EdgeFilter.property("test").eq(1i64); + let n1 = g.node(1).unwrap().filter(filter).unwrap(); + assert_eq!( + n1.edges().id().collect_vec(), + vec![(GID::U64(1), GID::U64(2))] + ); + let n2 = g + .node(2) + .unwrap() + .filter(EdgeFilter.property("test").gt(1i64)) + .unwrap(); + assert_eq!( + n2.edges().id().collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + } -#[test] -fn test_filter() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("test", 1i64)], None).unwrap(); - g.add_edge(1, 2, 3, [("test", 2i64)], None).unwrap(); - - let filter = EdgeFilter.property("test").eq(1i64); - let gf = g.filter(filter).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(1), GID::U64(2))] - ); - let gf = g.filter(EdgeFilter.property("test").gt(1i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); -} + #[test] + fn test_filter() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("test", 1i64)], None).unwrap(); + g.add_edge(1, 2, 3, [("test", 2i64)], None).unwrap(); + + let filter = EdgeFilter.property("test").eq(1i64); + let gf = g.filter(filter).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(1), GID::U64(2))] + ); + let gf = g.filter(EdgeFilter.property("test").gt(1i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + } -#[test] -fn test_filter_gt() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() > v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); + #[test] + fn test_filter_gt() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() > v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } } - } - }); - }) -} + }); + }) + } -#[test] -fn test_filter_ge() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").ge(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() >= v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); + #[test] + fn test_filter_ge() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").ge(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() >= v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } } - } - }); - }) -} + }); + }) + } -#[test] -fn test_filter_lt() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").lt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() < v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); + #[test] + fn test_filter_lt() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").lt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() < v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } } - } - }); - }) -} + }); + }) + } -#[test] -fn test_filter_le() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").le(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() <= v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); + #[test] + fn test_filter_le() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").le(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() <= v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } } - } - }); - }) -} + }); + }) + } -#[test] -fn test_filter_eq() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() == v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); + #[test] + fn test_filter_eq() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() == v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } } - } - }); - }) -} + }); + }) + } -#[test] -fn test_filter_ne() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").ne(v); - assert_ok_or_missing_edges(&edges, g.filter(filter), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() != v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); + #[test] + fn test_filter_ne() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").ne(v); + assert_ok_or_missing_edges(&edges, g.filter(filter), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() != v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } } + }); + }) + } + + #[test] + fn test_graph_materialise_window() { + proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { + let g = build_graph_from_edge_list(&edges); + for (src, dst, t) in edge_deletions { + g.delete_edge(t, src, dst, None).unwrap(); } - }); - }) -} + let filter = EdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.window(start, end).filter(filter.clone()), |filtered| { + let gwfm = filtered.materialize().unwrap(); + assert_graph_equal(&filtered, &gwfm); + }); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let gwf = filtered.window(start, end); + let gwfm = gwf.materialize().unwrap(); + assert_graph_equal(&gwf, &gwfm); + }); + }) + } -#[test] -fn test_graph_materialise_window() { - proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { - let g = build_graph_from_edge_list(&edges); + fn check_persistent_graph_mat_window( + edges: &[EdgeRow], + edge_deletions: Vec<(u64, u64, i64)>, + v: i64, + (start, end): (i64, i64), + ) { + let g = build_graph_from_edge_list(edges); + let g = g.persistent_graph(); for (src, dst, t) in edge_deletions { g.delete_edge(t, src, dst, None).unwrap(); } let filter = EdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.window(start, end).filter(filter.clone()), |filtered| { - let gwfm = filtered.materialize().unwrap(); - assert_graph_equal(&filtered, &gwfm); - }); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let gwf = filtered.window(start, end); - let gwfm = gwf.materialize().unwrap(); - assert_graph_equal(&gwf, &gwfm); + assert_ok_or_missing_edges( + edges, + g.window(start, end).filter(filter.clone()), + |filtered| { + let gwfm = filtered.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&filtered, &gwfm); + }, + ); + assert_ok_or_missing_edges(edges, g.filter(filter.clone()), |filtered| { + let gfw = filtered.window(start, end); + let gfwm = gfw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gfw, &gfwm); }); - }) -} - -fn check_persistent_graph_mat_window( - edges: &[EdgeRow], - edge_deletions: Vec<(u64, u64, i64)>, - v: i64, - (start, end): (i64, i64), -) { - let g = build_graph_from_edge_list(edges); - let g = g.persistent_graph(); - for (src, dst, t) in edge_deletions { - g.delete_edge(t, src, dst, None).unwrap(); } - let filter = EdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges( - edges, - g.window(start, end).filter(filter.clone()), - |filtered| { - let gwfm = filtered.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&filtered, &gwfm); - }, - ); - assert_ok_or_missing_edges(edges, g.filter(filter.clone()), |filtered| { - let gfw = filtered.window(start, end); - let gfwm = gfw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gfw, &gfwm); - }); -} -#[test] -fn test_persistent_graph_materialise_window() { - proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { - check_persistent_graph_mat_window(&edges, edge_deletions, v, (start, end)); - }) -} + #[test] + fn test_persistent_graph_materialise_window() { + proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { + check_persistent_graph_mat_window(&edges, edge_deletions, v, (start, end)); + }) + } -#[test] -fn simplte_graph_materialize_window() { - let edges = [(0, 0, 0, "".to_owned(), 0), (0, 0, 0, "".to_owned(), 0)]; - let edge_deletions = vec![]; - let start_end = (1, 2); - let v = -1; - check_persistent_graph_mat_window(&edges, edge_deletions, v, start_end); -} + #[test] + fn simplte_graph_materialize_window() { + let edges = [(0, 0, 0, "".to_owned(), 0), (0, 0, 0, "".to_owned(), 0)]; + let edge_deletions = vec![]; + let start_end = (1, 2); + let v = -1; + check_persistent_graph_mat_window(&edges, edge_deletions, v, start_end); + } -#[test] -fn test_single_unfiltered_edge_empty_window_persistent() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.delete_edge(10, 0, 1, None).unwrap(); - let gw = g - .filter(EdgeFilter.property("test").gt(0i64)) - .unwrap() - .window(0, 0); - - assert_eq!(gw.count_edges(), 0); - let expected = PersistentGraph::new(); - expected - .write_session() - .unwrap() - .resolve_edge_property("test", PropType::I64, false) - .unwrap(); - expected.resolve_layer(None).unwrap(); - assert_graph_equal(&gw, &expected) -} + #[test] + fn test_single_unfiltered_edge_empty_window_persistent() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.delete_edge(10, 0, 1, None).unwrap(); + let gw = g + .filter(EdgeFilter.property("test").gt(0i64)) + .unwrap() + .window(0, 0); + + assert_eq!(gw.count_edges(), 0); + let expected = PersistentGraph::new(); + expected + .write_session() + .unwrap() + .resolve_edge_property("test", PropType::I64, false) + .unwrap(); + expected.resolve_layer(None).unwrap(); + assert_graph_equal(&gw, &expected) + } -#[test] -fn test_single_deleted_edge_window_persistent() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.delete_edge(1, 0, 1, None).unwrap(); - let gw = g - .filter(EdgeFilter.property("test").gt(0i64)) - .unwrap() - .window(0, 2); - let gm = gw.materialize().unwrap(); + #[test] + fn test_single_deleted_edge_window_persistent() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.delete_edge(1, 0, 1, None).unwrap(); + let gw = g + .filter(EdgeFilter.property("test").gt(0i64)) + .unwrap() + .window(0, 2); + let gm = gw.materialize().unwrap(); - assert_eq!(gw.count_edges(), 1); - assert_eq!(gw.count_temporal_edges(), 1); + assert_eq!(gw.count_edges(), 1); + assert_eq!(gw.count_temporal_edges(), 1); - assert_eq!(gw.node(0).unwrap().edge_history_count(), 2); - assert_eq!(gw.node(0).unwrap().after(0).edge_history_count(), 1); + assert_eq!(gw.node(0).unwrap().edge_history_count(), 2); + assert_eq!(gw.node(0).unwrap().after(0).edge_history_count(), 1); - assert_persistent_materialize_graph_equal(&gw, &gm) -} + assert_persistent_materialize_graph_equal(&gw, &gm) + } -#[test] -fn test_single_unfiltered_edge_window_persistent_2() { - let g = PersistentGraph::new(); - g.add_edge(1, 0, 1, [("test", 1i64)], None).unwrap(); - g.delete_edge(0, 0, 0, None).unwrap(); - - let gwf = g - .window(-1, 2) - .filter(EdgeFilter.property("test").gt(0i64)) - .unwrap(); - assert!(gwf.has_edge(0, 1)); - assert!(!gwf.has_edge(0, 0)); - assert_eq!(gwf.node(0).unwrap().earliest_time().map(|t| t.t()), Some(1)); - assert_persistent_materialize_graph_equal(&gwf, &gwf.materialize().unwrap()); - - let gfw = g - .filter(EdgeFilter.property("test").gt(0i64)) - .unwrap() - .window(-1, 2); - let gm = gfw.materialize().unwrap(); - - assert_eq!(gfw.count_edges(), 1); - assert_eq!(gfw.count_temporal_edges(), 1); - - assert_eq!(gfw.node(0).unwrap().edge_history_count(), 1); - assert_eq!(gfw.node(0).unwrap().after(0).edge_history_count(), 1); - - assert_persistent_materialize_graph_equal(&gfw, &gm) + #[test] + fn test_single_unfiltered_edge_window_persistent_2() { + let g = PersistentGraph::new(); + g.add_edge(1, 0, 1, [("test", 1i64)], None).unwrap(); + g.delete_edge(0, 0, 0, None).unwrap(); + + let gwf = g + .window(-1, 2) + .filter(EdgeFilter.property("test").gt(0i64)) + .unwrap(); + assert!(gwf.has_edge(0, 1)); + assert!(!gwf.has_edge(0, 0)); + assert_eq!(gwf.node(0).unwrap().earliest_time().map(|t| t.t()), Some(1)); + assert_persistent_materialize_graph_equal(&gwf, &gwf.materialize().unwrap()); + + let gfw = g + .filter(EdgeFilter.property("test").gt(0i64)) + .unwrap() + .window(-1, 2); + let gm = gfw.materialize().unwrap(); + + assert_eq!(gfw.count_edges(), 1); + assert_eq!(gfw.count_temporal_edges(), 1); + + assert_eq!(gfw.node(0).unwrap().edge_history_count(), 1); + assert_eq!(gfw.node(0).unwrap().after(0).edge_history_count(), 1); + + assert_persistent_materialize_graph_equal(&gfw, &gm) + } } diff --git a/raphtory/tests/exploded_edge_property_filter.rs b/raphtory/tests/exploded_edge_property_filter.rs index 8f993e8f24..e5bc97d425 100644 --- a/raphtory/tests/exploded_edge_property_filter.rs +++ b/raphtory/tests/exploded_edge_property_filter.rs @@ -1,675 +1,678 @@ -use itertools::Itertools; -use proptest::{arbitrary::any, proptest}; -use raphtory::{ - db::{ - api::view::{Filter, StaticGraphViewOps}, - graph::{ - assertions::assert_ok_or_missing_edges, - edge::EdgeView, +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use proptest::{arbitrary::any, proptest}; + use raphtory::{ + db::{ + api::view::{Filter, StaticGraphViewOps}, graph::{ - assert_graph_equal, assert_node_equal, assert_nodes_equal, - assert_persistent_materialize_graph_equal, - }, - views::{ - deletion_graph::PersistentGraph, - filter::model::{ - property_filter::ops::PropertyFilterOps, ExplodedEdgeFilter, - PropertyFilterFactory, + assertions::assert_ok_or_missing_edges, + edge::EdgeView, + graph::{ + assert_graph_equal, assert_node_equal, assert_nodes_equal, + assert_persistent_materialize_graph_equal, + }, + views::{ + deletion_graph::PersistentGraph, + filter::model::{ + property_filter::ops::PropertyFilterOps, ExplodedEdgeFilter, + PropertyFilterFactory, + }, }, }, }, - }, - prelude::*, - test_utils::{ - build_edge_deletions, build_edge_list, build_edge_list_with_deletions, - build_graph_from_edge_list, build_window, Update, - }, -}; -use raphtory_api::core::{ - entities::properties::prop::PropType, - storage::{arc_str::ArcStr, timeindex::AsTime}, -}; -use raphtory_core::entities::nodes::node_ref::AsNodeRef; -use raphtory_storage::{ - core_ops::CoreGraphOps, - mutation::addition_ops::{InternalAdditionOps, SessionAdditionOps}, -}; -use std::collections::HashMap; - -fn build_filtered_graph( - edges: &[(u64, u64, i64, String, i64)], - filter: impl Fn(i64) -> bool, -) -> Graph { - let g = Graph::new(); - for (index, (src, dst, t, str_prop, int_prop)) in edges.iter().enumerate() { - if filter(*int_prop) { - g.add_edge( - (*t, index), - *src, - *dst, - [ - ("str_prop", Prop::str(str_prop.as_ref())), - ("int_prop", Prop::I64(*int_prop)), - ], - None, - ) - .unwrap(); + prelude::*, + test_utils::{ + build_edge_deletions, build_edge_list, build_edge_list_with_deletions, + build_graph_from_edge_list, build_window, Update, + }, + }; + use raphtory_api::core::{ + entities::properties::prop::PropType, + storage::{arc_str::ArcStr, timeindex::AsTime}, + }; + use raphtory_core::entities::nodes::node_ref::AsNodeRef; + use raphtory_storage::{ + core_ops::CoreGraphOps, + mutation::addition_ops::{InternalAdditionOps, SessionAdditionOps}, + }; + use std::collections::HashMap; + + fn build_filtered_graph( + edges: &[(u64, u64, i64, String, i64)], + filter: impl Fn(i64) -> bool, + ) -> Graph { + let g = Graph::new(); + for (index, (src, dst, t, str_prop, int_prop)) in edges.iter().enumerate() { + if filter(*int_prop) { + g.add_edge( + (*t, index), + *src, + *dst, + [ + ("str_prop", Prop::str(str_prop.as_ref())), + ("int_prop", Prop::I64(*int_prop)), + ], + None, + ) + .unwrap(); + } } + if !edges.is_empty() { + g.resolve_layer(None).unwrap(); + } + g } - if !edges.is_empty() { - g.resolve_layer(None).unwrap(); - } - g -} -fn build_filtered_nodes_graph( - edges: &[(u64, u64, i64, String, i64)], - filter: impl Fn(i64) -> bool, -) -> Graph { - let g = Graph::new(); - for (src, dst, t, str_prop, int_prop) in edges { - if filter(*int_prop) { - g.add_edge( - *t, - *src, - *dst, - [ - ("str_prop", str_prop.as_str().into()), - ("int_prop", Prop::I64(*int_prop)), - ], - None, - ) - .unwrap(); + fn build_filtered_nodes_graph( + edges: &[(u64, u64, i64, String, i64)], + filter: impl Fn(i64) -> bool, + ) -> Graph { + let g = Graph::new(); + for (src, dst, t, str_prop, int_prop) in edges { + if filter(*int_prop) { + g.add_edge( + *t, + *src, + *dst, + [ + ("str_prop", str_prop.as_str().into()), + ("int_prop", Prop::I64(*int_prop)), + ], + None, + ) + .unwrap(); + } + g.atomic_add_node(src.as_node_ref()).unwrap(); + g.atomic_add_node(dst.as_node_ref()).unwrap(); } - g.atomic_add_node(src.as_node_ref()).unwrap(); - g.atomic_add_node(dst.as_node_ref()).unwrap(); - } - if !edges.is_empty() { - g.resolve_layer(None).unwrap(); + if !edges.is_empty() { + g.resolve_layer(None).unwrap(); + } + g } - g -} -fn build_filtered_persistent_graph( - edges: HashMap<(u64, u64), Vec<(i64, Update)>>, - filter: impl Fn(i64) -> bool, -) -> (PersistentGraph, PersistentGraph) { - let g = PersistentGraph::new(); - let g_filtered = PersistentGraph::new(); - if !edges.iter().all(|(_, v)| v.is_empty()) { - g_filtered.resolve_layer(None).unwrap(); - } - for ((src, dst), updates) in edges { - for (t, update) in updates { - match update { - Update::Deletion => { - g.delete_edge(t, src, dst, None).unwrap(); - g_filtered.delete_edge(t, src, dst, None).unwrap(); - } - Update::Addition(str_prop, int_prop) => { - g.add_edge( - t, - src, - dst, - [ - ("str_prop", str_prop.clone().into()), - ("int_prop", Prop::I64(int_prop)), - ], - None, - ) - .unwrap(); - if filter(int_prop) { - g_filtered - .add_edge( - t, - src, - dst, - [ - ("str_prop", str_prop.into()), - ("int_prop", Prop::I64(int_prop)), - ], - None, - ) - .unwrap(); - } else { + fn build_filtered_persistent_graph( + edges: HashMap<(u64, u64), Vec<(i64, Update)>>, + filter: impl Fn(i64) -> bool, + ) -> (PersistentGraph, PersistentGraph) { + let g = PersistentGraph::new(); + let g_filtered = PersistentGraph::new(); + if !edges.iter().all(|(_, v)| v.is_empty()) { + g_filtered.resolve_layer(None).unwrap(); + } + for ((src, dst), updates) in edges { + for (t, update) in updates { + match update { + Update::Deletion => { + g.delete_edge(t, src, dst, None).unwrap(); g_filtered.delete_edge(t, src, dst, None).unwrap(); - // properties still exist after filtering - let session = g_filtered.write_session().unwrap(); - session - .resolve_edge_property("str_prop", PropType::Str, false) - .unwrap(); - session - .resolve_edge_property("int_prop", PropType::I64, false) - .unwrap(); + } + Update::Addition(str_prop, int_prop) => { + g.add_edge( + t, + src, + dst, + [ + ("str_prop", str_prop.clone().into()), + ("int_prop", Prop::I64(int_prop)), + ], + None, + ) + .unwrap(); + if filter(int_prop) { + g_filtered + .add_edge( + t, + src, + dst, + [ + ("str_prop", str_prop.into()), + ("int_prop", Prop::I64(int_prop)), + ], + None, + ) + .unwrap(); + } else { + g_filtered.delete_edge(t, src, dst, None).unwrap(); + // properties still exist after filtering + let session = g_filtered.write_session().unwrap(); + session + .resolve_edge_property("str_prop", PropType::Str, false) + .unwrap(); + session + .resolve_edge_property("int_prop", PropType::I64, false) + .unwrap(); + } } } } } + (g, g_filtered) } - (g, g_filtered) -} -fn edge_attr( - e: &EdgeView, -) -> (String, String, Option, Option) { - let src = e.src().name(); - let dst = e.dst().name(); - let int_prop = e.properties().get("int_prop"); - let str_prop = e.properties().get("str_prop"); - (src, dst, int_prop.into_i64(), str_prop.into_str()) -} + fn edge_attr( + e: &EdgeView, + ) -> (String, String, Option, Option) { + let src = e.src().name(); + let dst = e.dst().name(); + let int_prop = e.properties().get("int_prop"); + let str_prop = e.properties().get("str_prop"); + (src, dst, int_prop.into_i64(), str_prop.into_str()) + } -#[test] -fn test_filter_gt() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv > v); - assert_graph_equal(&filtered, &expected_filtered_g); - #[cfg(feature = "search")] - { - let search_ee = g.search_exploded_edges(filter, 100, 0).unwrap(); - let filter_ee = filtered.edges().explode().collect(); - let from_search = search_ee.iter().map(edge_attr).collect_vec(); - let from_filter = filter_ee.iter().map(edge_attr).collect_vec(); - assert_eq!(from_search, from_filter); + #[test] + fn test_filter_gt() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv > v); + assert_graph_equal(&filtered, &expected_filtered_g); + #[cfg(feature = "search")] + { + let search_ee = g.search_exploded_edges(filter, 100, 0).unwrap(); + let filter_ee = filtered.edges().explode().collect(); + let from_search = search_ee.iter().map(edge_attr).collect_vec(); + let from_filter = filter_ee.iter().map(edge_attr).collect_vec(); + assert_eq!(from_search, from_filter); + } + }); + }) + } + + #[test] + fn test_filter_gt_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv > v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").gt(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + assert_graph_equal(&filtered, &expected_filtered_g); + } else { + assert!(filtered.is_err()) } - }); - }) -} + }) + } -#[test] -fn test_filter_gt_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv > v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").gt(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) -} + #[test] + fn test_one_edge() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("int_prop", 0i64)], None).unwrap(); + let filtered = g + .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) + .unwrap(); + let gf = Graph::new(); + gf.resolve_layer(None).unwrap(); + assert_eq!(filtered.count_nodes(), 0); + assert_eq!(filtered.count_edges(), 0); + assert_graph_equal(&filtered, &gf); + } -#[test] -fn test_one_edge() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("int_prop", 0i64)], None).unwrap(); - let filtered = g - .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) - .unwrap(); - let gf = Graph::new(); - gf.resolve_layer(None).unwrap(); - assert_eq!(filtered.count_nodes(), 0); - assert_eq!(filtered.count_edges(), 0); - assert_graph_equal(&filtered, &gf); -} + #[test] + fn test_filter_ge() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").ge(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv >= v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) + } -#[test] -fn test_filter_ge() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").ge(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv >= v); + #[test] + fn test_filter_ge_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv >= v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + if p.is_some() { + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").ge(v) + ).unwrap(); assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) -} + } -#[test] -fn test_filter_ge_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv >= v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - if p.is_some() { - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").ge(v) - ).unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } + }) + } - }) -} + #[test] + fn test_filter_persistent_single_filtered_edge() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, [("test", 1i64)], None).unwrap(); + let gf = g + .filter(ExplodedEdgeFilter.property("test").gt(1i64)) + .unwrap(); -#[test] -fn test_filter_persistent_single_filtered_edge() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, [("test", 1i64)], None).unwrap(); - let gf = g - .filter(ExplodedEdgeFilter.property("test").gt(1i64)) - .unwrap(); - - assert_eq!(gf.count_edges(), 1); - assert_eq!(gf.count_temporal_edges(), 0); - - let expected = PersistentGraph::new(); - expected.delete_edge(0, 0, 0, None).unwrap(); - //the property still exists! - expected - .write_session() - .unwrap() - .resolve_edge_property("test", PropType::I64, false) - .unwrap(); - - assert_graph_equal(&gf, &expected); -} + assert_eq!(gf.count_edges(), 1); + assert_eq!(gf.count_temporal_edges(), 0); -#[test] -fn test_filter_lt() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").lt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv < v); - assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) -} + let expected = PersistentGraph::new(); + expected.delete_edge(0, 0, 0, None).unwrap(); + //the property still exists! + expected + .write_session() + .unwrap() + .resolve_edge_property("test", PropType::I64, false) + .unwrap(); -#[test] -fn test_filter_lt_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv < v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").lt(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) -} + assert_graph_equal(&gf, &expected); + } -#[test] -fn test_filter_le() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").le(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv <= v); + #[test] + fn test_filter_lt() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").lt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv < v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) + } + + #[test] + fn test_filter_lt_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv < v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").lt(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) -} + } else { + assert!(filtered.is_err()) + } + }) + } -#[test] -fn test_filter_le_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv <= v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").le(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) -} + #[test] + fn test_filter_le() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").le(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv <= v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) + } -#[test] -fn test_filter_eq() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); + #[test] + fn test_filter_le_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv <= v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").le(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) -} + } else { + assert!(filtered.is_err()) + } + }) + } -#[test] -fn test_filter_eq_one_edge() { - let g = Graph::new(); - g.add_edge(0, 0, 0, [("int_prop", Prop::I64(0))], None) - .unwrap(); - let filter = ExplodedEdgeFilter.property("int_prop").eq(0i64); - assert_graph_equal(&g.filter(filter.clone()).unwrap(), &g); -} + #[test] + fn test_filter_eq() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) + } -#[test] -fn test_filter_eq_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv == v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").eq(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) -} + #[test] + fn test_filter_eq_one_edge() { + let g = Graph::new(); + g.add_edge(0, 0, 0, [("int_prop", Prop::I64(0))], None) + .unwrap(); + let filter = ExplodedEdgeFilter.property("int_prop").eq(0i64); + assert_graph_equal(&g.filter(filter.clone()).unwrap(), &g); + } -#[test] -fn test_filter_ne() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").ne(v); - assert_ok_or_missing_edges(&edges, g.filter(filter), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv != v); + #[test] + fn test_filter_eq_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv == v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").eq(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) -} + } else { + assert!(filtered.is_err()) + } + }) + } -#[test] -fn test_filter_ne_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv != v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").ne(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) -} + #[test] + fn test_filter_ne() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").ne(v); + assert_ok_or_missing_edges(&edges, g.filter(filter), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv != v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) + } -#[test] -fn test_filter_window() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::(), (start, end) in build_window() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); - assert_graph_equal(&filtered.window(start, end), &expected_filtered_g.window(start, end)); + #[test] + fn test_filter_ne_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv != v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").ne(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + assert_graph_equal(&filtered, &expected_filtered_g); + } else { + assert!(filtered.is_err()) + } + }) + } + + #[test] + fn test_filter_window() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::(), (start, end) in build_window() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); + assert_graph_equal(&filtered.window(start, end), &expected_filtered_g.window(start, end)); + }); }); - }) -} + } -#[test] -fn test_filter_window_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::(), (start, end) in build_window() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv >= v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").ge(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered.window(start, end), &expected_filtered_g.window(start, end)); - } else { - assert!(filtered.is_err()) - } - }) -} + #[test] + fn test_filter_window_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::(), (start, end) in build_window() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv >= v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").ge(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + assert_graph_equal(&filtered.window(start, end), &expected_filtered_g.window(start, end)); + } else { + assert!(filtered.is_err()) + } + }) + } -#[test] -fn test_filter_materialise_is_consistent() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + #[test] + fn test_filter_materialise_is_consistent() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let mat = filtered.materialize().unwrap(); + assert_graph_equal(&filtered, &mat); + }); + }) + } + + #[test] + fn test_filter_persistent_materialise_is_consistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected) = build_filtered_persistent_graph(edges, |vv| vv >= v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").ge(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); let mat = filtered.materialize().unwrap(); assert_graph_equal(&filtered, &mat); - }); - }) -} + assert_graph_equal(&expected, &mat); + } else { + assert!(filtered.is_err()) + } + }) + } -#[test] -fn test_filter_persistent_materialise_is_consistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected) = build_filtered_persistent_graph(edges, |vv| vv >= v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").ge(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - let mat = filtered.materialize().unwrap(); - assert_graph_equal(&filtered, &mat); - assert_graph_equal(&expected, &mat); - } else { - assert!(filtered.is_err()) - } - }) -} + #[test] + #[ignore = "need a way to add a node without timestamp"] + fn test_filter_on_nodes() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.nodes().filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_nodes_graph(&edges, |vv| vv == v); + assert_nodes_equal(&filtered, &expected_filtered_g.nodes()); + }); + }) + } -#[test] -#[ignore = "need a way to add a node without timestamp"] -fn test_filter_on_nodes() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { + #[test] + #[ignore = "need a way to add a node without timestamp"] + fn test_filter_on_nodes_simple() { + let edges = [(1u64, 2u64, 0i64, "a".to_string(), 10i64)]; + let v = -1; let g = build_graph_from_edge_list(&edges); let filter = ExplodedEdgeFilter.property("int_prop").eq(v); assert_ok_or_missing_edges(&edges, g.nodes().filter(filter.clone()), |filtered| { let expected_filtered_g = build_filtered_nodes_graph(&edges, |vv| vv == v); assert_nodes_equal(&filtered, &expected_filtered_g.nodes()); }); - }) -} - -#[test] -#[ignore = "need a way to add a node without timestamp"] -fn test_filter_on_nodes_simple() { - let edges = [(1u64, 2u64, 0i64, "a".to_string(), 10i64)]; - let v = -1; - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.nodes().filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_nodes_graph(&edges, |vv| vv == v); - assert_nodes_equal(&filtered, &expected_filtered_g.nodes()); - }); -} + } -#[test] -fn test_filter_on_node() { - proptest!(|( - edges in build_edge_list(100, 2), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - if let Some(node) = g.node(0) { - let filtered_node = node.filter( - ExplodedEdgeFilter.property("int_prop").eq(v) - ).unwrap(); - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); - if filtered_node.degree() == 0 { - // should be filtered out - assert!(expected_filtered_g.node(0).is_none()) - } else { - assert_node_equal(filtered_node, expected_filtered_g.node(0).unwrap()) + #[test] + fn test_filter_on_node() { + proptest!(|( + edges in build_edge_list(100, 2), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + if let Some(node) = g.node(0) { + let filtered_node = node.filter( + ExplodedEdgeFilter.property("int_prop").eq(v) + ).unwrap(); + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); + if filtered_node.degree() == 0 { + // should be filtered out + assert!(expected_filtered_g.node(0).is_none()) + } else { + assert_node_equal(filtered_node, expected_filtered_g.node(0).unwrap()) + } } - } - }) -} - -#[test] -fn test_filter_materialise_window_is_consistent() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::(), (start, end) in build_window() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let left = filtered.window(start, end); - let right = filtered.window(start, end).materialize().unwrap(); - assert_graph_equal(&left, &right); - }); - }) -} + }) + } -#[test] -fn test_persistent_graph() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, [("int_prop", 0i64)], None).unwrap(); - g.delete_edge(2, 1, 2, None).unwrap(); - g.add_edge(5, 1, 2, [("int_prop", 5i64)], None).unwrap(); - g.delete_edge(7, 1, 2, None).unwrap(); - - let edges = g - .node(1) - .unwrap() - .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) - .unwrap() - .edges() - .explode() - .collect(); - println!("{:?}", edges); - - assert_eq!(edges.len(), 1); - let gf = g - .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) - .unwrap(); - let gfm = gf.materialize().unwrap(); - - assert_graph_equal(&gf, &gfm); // check materialise is consistent -} + #[test] + fn test_filter_materialise_window_is_consistent() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::(), (start, end) in build_window() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let left = filtered.window(start, end); + let right = filtered.window(start, end).materialize().unwrap(); + assert_graph_equal(&left, &right); + }); + }) + } -#[test] -fn test_persistent_graph_explode_semantics() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 0i64)], None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.add_edge(5, 0, 1, [("test", 5i64)], None).unwrap(); - g.delete_edge(10, 0, 1, None).unwrap(); - - let gf = g - .filter(ExplodedEdgeFilter.property("test").ne(2i64)) - .unwrap(); - assert_eq!( - gf.edges() + #[test] + fn test_persistent_graph() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, [("int_prop", 0i64)], None).unwrap(); + g.delete_edge(2, 1, 2, None).unwrap(); + g.add_edge(5, 1, 2, [("int_prop", 5i64)], None).unwrap(); + g.delete_edge(7, 1, 2, None).unwrap(); + + let edges = g + .node(1) + .unwrap() + .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) + .unwrap() + .edges() .explode() - .earliest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(0i64), Some(5i64)] - ); - assert_eq!( - gf.edges() - .explode() - .latest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(2i64), Some(10i64)] - ); -} + .collect(); + println!("{:?}", edges); -#[test] -fn test_persistent_graph_materialise() { - proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::())| { - let g = build_graph_from_edge_list(&edges); - let g = g.persistent_graph(); - for (src, dst, t) in edge_deletions { - g.delete_edge(t, src, dst, None).unwrap(); - } - let filter = ExplodedEdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let gfm = filtered.materialize().unwrap(); - assert_graph_equal(&filtered, &gfm) - }); - }) -} + assert_eq!(edges.len(), 1); + let gf = g + .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) + .unwrap(); + let gfm = gf.materialize().unwrap(); -#[test] -fn test_single_filtered_edge_persistent() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, [("test", 0i64)], None).unwrap(); - let gf = g - .filter(ExplodedEdgeFilter.property("test").gt(0i64)) - .unwrap(); - let gfm = gf.materialize().unwrap(); - dbg!(&gfm); - assert_eq!(gf.node(0).unwrap().out_degree(), 1); - assert_eq!(gfm.node(0).unwrap().out_degree(), 1); - assert_graph_equal(&gf, &gfm); -} + assert_graph_equal(&gf, &gfm); // check materialise is consistent + } -#[test] -fn test_persistent_graph_materialise_window() { - proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { - let g = build_graph_from_edge_list(&edges); - let g = g.persistent_graph(); - for (src, dst, t) in edge_deletions { - g.delete_edge(t, src, dst, None).unwrap(); - } - let filter = ExplodedEdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.window(start, end).filter(filter.clone()), |filtered| { - let gwfm = filtered.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&filtered, &gwfm); - }); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let gfw = filtered.window(start, end); - let gfwm = gfw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gfw, &gfwm); - }); - }) -} + #[test] + fn test_persistent_graph_explode_semantics() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 0i64)], None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.add_edge(5, 0, 1, [("test", 5i64)], None).unwrap(); + g.delete_edge(10, 0, 1, None).unwrap(); + + let gf = g + .filter(ExplodedEdgeFilter.property("test").ne(2i64)) + .unwrap(); + assert_eq!( + gf.edges() + .explode() + .earliest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(0i64), Some(5i64)] + ); + assert_eq!( + gf.edges() + .explode() + .latest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(2i64), Some(10i64)] + ); + } + + #[test] + fn test_persistent_graph_materialise() { + proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::())| { + let g = build_graph_from_edge_list(&edges); + let g = g.persistent_graph(); + for (src, dst, t) in edge_deletions { + g.delete_edge(t, src, dst, None).unwrap(); + } + let filter = ExplodedEdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let gfm = filtered.materialize().unwrap(); + assert_graph_equal(&filtered, &gfm) + }); + }) + } + + #[test] + fn test_single_filtered_edge_persistent() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, [("test", 0i64)], None).unwrap(); + let gf = g + .filter(ExplodedEdgeFilter.property("test").gt(0i64)) + .unwrap(); + let gfm = gf.materialize().unwrap(); + dbg!(&gfm); + assert_eq!(gf.node(0).unwrap().out_degree(), 1); + assert_eq!(gfm.node(0).unwrap().out_degree(), 1); + assert_graph_equal(&gf, &gfm); + } + + #[test] + fn test_persistent_graph_materialise_window() { + proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { + let g = build_graph_from_edge_list(&edges); + let g = g.persistent_graph(); + for (src, dst, t) in edge_deletions { + g.delete_edge(t, src, dst, None).unwrap(); + } + let filter = ExplodedEdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.window(start, end).filter(filter.clone()), |filtered| { + let gwfm = filtered.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&filtered, &gwfm); + }); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let gfw = filtered.window(start, end); + let gfwm = gfw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gfw, &gfwm); + }); + }) + } + + #[test] + fn test_persistent_failure() { + let g = PersistentGraph::new(); + g.add_edge(-1, 0, 1, [("test", Prop::I32(-1))], None) + .unwrap(); + g.add_edge(0, 0, 1, [("test", Prop::I32(1))], None).unwrap(); + + let gwf = g + .filter(ExplodedEdgeFilter.property("test").gt(0)) + .unwrap() + .window(-1, 0); + assert_eq!(gwf.count_nodes(), 0); + assert_eq!(gwf.count_edges(), 0); + let gm = gwf.materialize().unwrap(); + + assert_persistent_materialize_graph_equal(&gwf, &gm); -#[test] -fn test_persistent_failure() { - let g = PersistentGraph::new(); - g.add_edge(-1, 0, 1, [("test", Prop::I32(-1))], None) - .unwrap(); - g.add_edge(0, 0, 1, [("test", Prop::I32(1))], None).unwrap(); - - let gwf = g - .filter(ExplodedEdgeFilter.property("test").gt(0)) - .unwrap() - .window(-1, 0); - assert_eq!(gwf.count_nodes(), 0); - assert_eq!(gwf.count_edges(), 0); - let gm = gwf.materialize().unwrap(); - - assert_persistent_materialize_graph_equal(&gwf, &gm); - - let gfw = g - .window(-1, 0) - .filter(ExplodedEdgeFilter.property("test").gt(0)) - .unwrap(); - assert_eq!(gfw.count_edges(), 0); - let gm = gfw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gfw, &gm); + let gfw = g + .window(-1, 0) + .filter(ExplodedEdgeFilter.property("test").gt(0)) + .unwrap(); + assert_eq!(gfw.count_edges(), 0); + let gm = gfw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gfw, &gm); + } } diff --git a/raphtory/tests/node_property_filter.rs b/raphtory/tests/node_property_filter.rs index cb2385efd7..ca87e0e707 100644 --- a/raphtory/tests/node_property_filter.rs +++ b/raphtory/tests/node_property_filter.rs @@ -1,456 +1,459 @@ -use itertools::Itertools; -use proptest::{arbitrary::any, proptest}; -use raphtory::{ - db::{ - api::view::filter_ops::{Filter, NodeSelect}, - graph::{ - assertions::assert_ok_or_missing_nodes, - graph::assert_edges_equal, - views::filter::model::{ - node_filter::{ops::NodeFilterOps, NodeFilter}, - property_filter::ops::PropertyFilterOps, - ComposableFilter, PropertyFilterFactory, +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use proptest::{arbitrary::any, proptest}; + use raphtory::{ + db::{ + api::view::filter_ops::{Filter, NodeSelect}, + graph::{ + assertions::assert_ok_or_missing_nodes, + graph::assert_edges_equal, + views::filter::model::{ + node_filter::{ops::NodeFilterOps, NodeFilter}, + property_filter::ops::PropertyFilterOps, + ComposableFilter, PropertyFilterFactory, + }, }, }, - }, - prelude::*, - test_utils::{ - add_node_props, build_edge_list, build_graph_from_edge_list, build_node_props, - node_filtered_graph, - }, -}; - -#[test] -#[ignore] -// TODO: Enable this once fixed -fn test_node_filter_on_nodes() { - let g = Graph::new(); - g.add_node(0, "Jimi", [("band", "JH Experience")], None, None) - .unwrap(); - g.add_node(1, "John", [("band", "Dead & Company")], None, None) - .unwrap(); - g.add_node(2, "David", [("band", "Pink Floyd")], None, None) - .unwrap(); - - let filter_expr = NodeFilter::name() - .eq("John") - .and(NodeFilter.property("band").eq("Dead & Company")); - let filtered_nodes = g.nodes().filter(filter_expr).unwrap(); - - // filter_nodes doesn't filter the iterator, it only filters the view of the nodes which includes history, edges, etc. - assert_eq!( - filtered_nodes.name().collect::>(), - vec!["Jimi", "John", "David"] - ); - - // TODO: Bug! History isn't getting filtered - let res = filtered_nodes - .iter() - .map(|n| n.history()) - .collect::>(); - assert_eq!(res, vec![vec![], vec![1], vec![]]); - - // TODO: Bug! Properties aren't getting filtered - let res = filtered_nodes - .iter() - .map(|n| n.properties().get("band")) - .collect::>(); - assert_eq!(res, vec![None, Some(Prop::str("Dead & Company")), None]); - - g.add_edge(3, "John", "Jimi", NO_PROPS, None).unwrap(); - - let res = filtered_nodes - .iter() - .map(|n| n.out_neighbours().name().collect_vec()) - .collect::>(); - assert_eq!(res, vec![Vec::::new(), vec![], vec![]]); -} - -#[test] -fn test_node_property_filter_on_nodes() { - let g = Graph::new(); - g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); - g.add_node(0, 2, [("test", 2i64)], None, None).unwrap(); - g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); - g.add_node(1, 4, [("test", 4i64)], None, None).unwrap(); - - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); - g.add_edge(2, 3, 4, NO_PROPS, None).unwrap(); - g.add_edge(3, 4, 1, NO_PROPS, None).unwrap(); - - let n1 = g.node(1).unwrap(); - - assert_eq!( - n1.filter(NodeFilter.property("test").eq(1i64)) - .unwrap() - .edges() - .id() - .collect_vec(), - vec![] - ); - assert_eq!( - n1.filter(NodeFilter.property("test").eq(2i64)) - .unwrap() - .out_neighbours() - .id() - .collect_vec(), - vec![GID::U64(2)] - ); - - let n2 = g.node(2).unwrap(); - - assert_eq!( - n2.filter(NodeFilter.property("test").gt(1i64)) - .unwrap() - .neighbours() - .id() - .collect_vec(), - vec![GID::U64(3)] - ); - - assert_eq!( - n2.filter(NodeFilter.property("test").gt(0i64)) - .unwrap() - .neighbours() - .id() - .collect_vec(), - vec![GID::U64(1), GID::U64(3)] - ); - - let gp = g.persistent_graph(); - let n1p = gp.node(1).unwrap(); - - assert_eq!( - n1p.filter(NodeFilter.property("test").eq(1i64)) - .unwrap() - .edges() - .id() - .collect_vec(), - vec![] - ); - assert_eq!( - n1p.filter(NodeFilter.property("test").eq(2i64)) - .unwrap() - .out_neighbours() - .id() - .collect_vec(), - vec![GID::U64(2)] - ); - - let n2p = gp.node(2).unwrap(); - - assert_eq!( - n2p.filter(NodeFilter.property("test").gt(1i64)) - .unwrap() - .neighbours() - .id() - .collect_vec(), - vec![GID::U64(3)] - ); - - assert_eq!( - n2p.filter(NodeFilter.property("test").gt(0i64)) - .unwrap() - .neighbours() - .id() - .collect_vec(), - vec![GID::U64(1), GID::U64(3)] - ); -} - -#[test] -fn test_node_property_filter_path() { - let g = Graph::new(); - g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); - g.add_node(1, 2, [("test", 2i64)], None, None).unwrap(); - g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); - g.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); - - let filtered_nodes = g - .nodes() - .select(NodeFilter.property("test").gt(1i64)) - .unwrap(); - assert_eq!( - filtered_nodes - .out_neighbours() - .id() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, i)| i.sorted().collect_vec()) - .collect_vec(), - vec![vec![GID::U64(1), GID::U64(3)], vec![]] - ); - - assert_eq!( - filtered_nodes - .out_neighbours() - .degree() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, i)| i.collect_vec()) - .collect_vec(), - vec![vec![2, 2], vec![]] - ); - - let filtered_nodes_p = g - .persistent_graph() - .nodes() - .select(NodeFilter.property("test").gt(1i64)) - .unwrap(); - assert_eq!( - filtered_nodes_p - .out_neighbours() - .id() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, i)| i.sorted().collect_vec()) - .collect_vec(), - vec![vec![GID::U64(1), GID::U64(3)], vec![]] - ); -} - -#[test] -fn test_node_property_filter_on_graph() { - let g = Graph::new(); - g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); - g.add_node(1, 2, [("test", 2i64)], None, None).unwrap(); - g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); - g.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); - - let gf = g.filter(NodeFilter.property("test").eq(1i64)).unwrap(); - assert_eq!(gf.edges().id().collect_vec(), vec![]); - - let gf = g.filter(NodeFilter.property("test").gt(1i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - - let gf = g.filter(NodeFilter.property("test").lt(3i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(1), GID::U64(2)), (GID::U64(2), GID::U64(1))] - ); - - let gp = g.persistent_graph(); - let gf = gp.filter(NodeFilter.property("test").eq(1i64)).unwrap(); - assert_eq!(gf.edges().id().collect_vec(), vec![]); - - let gf = gp.filter(NodeFilter.property("test").gt(1i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - - let gf = gp.filter(NodeFilter.property("test").lt(3i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(1), GID::U64(2)), (GID::U64(2), GID::U64(1))] - ); -} - -#[test] -fn test_filter_gt() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").gt(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv > v).is_some() + prelude::*, + test_utils::{ + add_node_props, build_edge_list, build_graph_from_edge_list, build_node_props, + node_filtered_graph, + }, + }; + + #[test] + #[ignore] + // TODO: Enable this once fixed + fn test_node_filter_on_nodes() { + let g = Graph::new(); + g.add_node(0, "Jimi", [("band", "JH Experience")], None, None) + .unwrap(); + g.add_node(1, "John", [("band", "Dead & Company")], None, None) + .unwrap(); + g.add_node(2, "David", [("band", "Pink Floyd")], None, None) + .unwrap(); + + let filter_expr = NodeFilter::name() + .eq("John") + .and(NodeFilter.property("band").eq("Dead & Company")); + let filtered_nodes = g.nodes().filter(filter_expr).unwrap(); + + // filter_nodes doesn't filter the iterator, it only filters the view of the nodes which includes history, edges, etc. + assert_eq!( + filtered_nodes.name().collect::>(), + vec!["Jimi", "John", "David"] + ); + + // TODO: Bug! History isn't getting filtered + let res = filtered_nodes + .iter() + .map(|n| n.history()) + .collect::>(); + assert_eq!(res, vec![vec![], vec![1], vec![]]); + + // TODO: Bug! Properties aren't getting filtered + let res = filtered_nodes + .iter() + .map(|n| n.properties().get("band")) + .collect::>(); + assert_eq!(res, vec![None, Some(Prop::str("Dead & Company")), None]); + + g.add_edge(3, "John", "Jimi", NO_PROPS, None).unwrap(); + + let res = filtered_nodes + .iter() + .map(|n| n.out_neighbours().name().collect_vec()) + .collect::>(); + assert_eq!(res, vec![Vec::::new(), vec![], vec![]]); + } + + #[test] + fn test_node_property_filter_on_nodes() { + let g = Graph::new(); + g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); + g.add_node(0, 2, [("test", 2i64)], None, None).unwrap(); + g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); + g.add_node(1, 4, [("test", 4i64)], None, None).unwrap(); + + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); + g.add_edge(2, 3, 4, NO_PROPS, None).unwrap(); + g.add_edge(3, 4, 1, NO_PROPS, None).unwrap(); + + let n1 = g.node(1).unwrap(); + + assert_eq!( + n1.filter(NodeFilter.property("test").eq(1i64)) + .unwrap() + .edges() + .id() + .collect_vec(), + vec![] + ); + assert_eq!( + n1.filter(NodeFilter.property("test").eq(2i64)) + .unwrap() + .out_neighbours() + .id() + .collect_vec(), + vec![GID::U64(2)] + ); + + let n2 = g.node(2).unwrap(); + + assert_eq!( + n2.filter(NodeFilter.property("test").gt(1i64)) + .unwrap() + .neighbours() + .id() + .collect_vec(), + vec![GID::U64(3)] + ); + + assert_eq!( + n2.filter(NodeFilter.property("test").gt(0i64)) + .unwrap() + .neighbours() + .id() + .collect_vec(), + vec![GID::U64(1), GID::U64(3)] + ); + + let gp = g.persistent_graph(); + let n1p = gp.node(1).unwrap(); + + assert_eq!( + n1p.filter(NodeFilter.property("test").eq(1i64)) + .unwrap() + .edges() + .id() + .collect_vec(), + vec![] + ); + assert_eq!( + n1p.filter(NodeFilter.property("test").eq(2i64)) + .unwrap() + .out_neighbours() + .id() + .collect_vec(), + vec![GID::U64(2)] + ); + + let n2p = gp.node(2).unwrap(); + + assert_eq!( + n2p.filter(NodeFilter.property("test").gt(1i64)) + .unwrap() + .neighbours() + .id() + .collect_vec(), + vec![GID::U64(3)] + ); + + assert_eq!( + n2p.filter(NodeFilter.property("test").gt(0i64)) + .unwrap() + .neighbours() + .id() + .collect_vec(), + vec![GID::U64(1), GID::U64(3)] + ); + } + + #[test] + fn test_node_property_filter_path() { + let g = Graph::new(); + g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); + g.add_node(1, 2, [("test", 2i64)], None, None).unwrap(); + g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); + g.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); + + let filtered_nodes = g + .nodes() + .select(NodeFilter.property("test").gt(1i64)) + .unwrap(); + assert_eq!( + filtered_nodes + .out_neighbours() + .id() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, i)| i.sorted().collect_vec()) + .collect_vec(), + vec![vec![GID::U64(1), GID::U64(3)], vec![]] + ); + + assert_eq!( + filtered_nodes + .out_neighbours() + .degree() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, i)| i.collect_vec()) + .collect_vec(), + vec![vec![2, 2], vec![]] + ); + + let filtered_nodes_p = g + .persistent_graph() + .nodes() + .select(NodeFilter.property("test").gt(1i64)) + .unwrap(); + assert_eq!( + filtered_nodes_p + .out_neighbours() + .id() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, i)| i.sorted().collect_vec()) + .collect_vec(), + vec![vec![GID::U64(1), GID::U64(3)], vec![]] + ); + } + + #[test] + fn test_node_property_filter_on_graph() { + let g = Graph::new(); + g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); + g.add_node(1, 2, [("test", 2i64)], None, None).unwrap(); + g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); + g.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); + + let gf = g.filter(NodeFilter.property("test").eq(1i64)).unwrap(); + assert_eq!(gf.edges().id().collect_vec(), vec![]); + + let gf = g.filter(NodeFilter.property("test").gt(1i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + + let gf = g.filter(NodeFilter.property("test").lt(3i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(1), GID::U64(2)), (GID::U64(2), GID::U64(1))] + ); + + let gp = g.persistent_graph(); + let gf = gp.filter(NodeFilter.property("test").eq(1i64)).unwrap(); + assert_eq!(gf.edges().id().collect_vec(), vec![]); + + let gf = gp.filter(NodeFilter.property("test").gt(1i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + + let gf = gp.filter(NodeFilter.property("test").lt(3i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(1), GID::U64(2)), (GID::U64(2), GID::U64(1))] + ); + } + + #[test] + fn test_filter_gt() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").gt(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv > v).is_some() + }); + + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); }); - - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) -} - -#[test] -fn test_filter_ge() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").ge(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv >= v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) -} - -#[test] -fn test_filter_lt() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").lt(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv < v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) -} - -#[test] -fn test_filter_le() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").le(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv <= v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) -} - -#[test] -fn test_filter_eq() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").eq(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv == v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) -} - -#[test] -fn test_filter_ne() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").ne(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv != v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) -} - -#[test] -fn test_filter_is_some() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").is_some(); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + // FIXME: history filtering not working properly // assert_graph_equal(&filtered, &expected_g); - }); - }) -} - -#[test] -fn test_filter_is_none() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100) - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").is_none(); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.is_none() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + }); + }) + } + + #[test] + fn test_filter_ge() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").ge(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv >= v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) + } + + #[test] + fn test_filter_lt() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").lt(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv < v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); // FIXME: history filtering not working properly // assert_graph_equal(&filtered, &expected_g); - }); - }) -} - -#[test] -fn test_filter_is_none_simple_graph() { - let graph = Graph::new(); - graph - .add_node(1, 1, [("p1", 1), ("p2", 2)], Some("fire_nation"), None) - .unwrap(); - graph - .add_node(2, 1, [("p6", 6)], Some("fire_nation"), None) - .unwrap(); - graph - .add_node(2, 2, [("p4", 5)], Some("fire_nation"), None) - .unwrap(); - graph - .add_node(3, 3, [("p2", 4), ("p3", 3)], Some("water_tribe"), None) - .unwrap(); - - assert_eq!(graph.count_nodes(), 3); - - let filtered = graph.filter(NodeFilter.property("p2").is_none()).unwrap(); - let ids = filtered.nodes().name().collect_vec(); - - assert_eq!(ids, vec!["2"]); + }); + }) + } + + #[test] + fn test_filter_le() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").le(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv <= v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) + } + + #[test] + fn test_filter_eq() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").eq(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv == v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) + } + + #[test] + fn test_filter_ne() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").ne(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv != v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) + } + + #[test] + fn test_filter_is_some() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").is_some(); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) + } + + #[test] + fn test_filter_is_none() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100) + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").is_none(); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.is_none() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) + } + + #[test] + fn test_filter_is_none_simple_graph() { + let graph = Graph::new(); + graph + .add_node(1, 1, [("p1", 1), ("p2", 2)], Some("fire_nation"), None) + .unwrap(); + graph + .add_node(2, 1, [("p6", 6)], Some("fire_nation"), None) + .unwrap(); + graph + .add_node(2, 2, [("p4", 5)], Some("fire_nation"), None) + .unwrap(); + graph + .add_node(3, 3, [("p2", 4), ("p3", 3)], Some("water_tribe"), None) + .unwrap(); + + assert_eq!(graph.count_nodes(), 3); + + let filtered = graph.filter(NodeFilter.property("p2").is_none()).unwrap(); + let ids = filtered.nodes().name().collect_vec(); + + assert_eq!(ids, vec!["2"]); + } } diff --git a/raphtory/tests/node_test.rs b/raphtory/tests/node_test.rs index 2a2c607bc3..26250c0e9d 100644 --- a/raphtory/tests/node_test.rs +++ b/raphtory/tests/node_test.rs @@ -1,434 +1,437 @@ -use raphtory::{ - db::{ - api::view::Filter, - graph::views::filter::model::{NodeViewFilterOps, ViewWrapOps}, - }, - prelude::*, - test_storage, - test_utils::test_graph, -}; -use raphtory_api::core::storage::arc_str::ArcStr; -use raphtory_core::storage::timeindex::AsTime; -use std::collections::HashMap; - -#[test] -fn test_earliest_time() { - let graph = Graph::new(); - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - - // FIXME: Node add without properties not showing up (Issue #46) - test_graph(&graph, |graph| { - let view = graph.before(2); - assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 0); - assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 1); - - let view = graph.before(3); - assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 0); - assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 2); - - let view = graph.after(0); - assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 1); - assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 2); - - let view = graph.after(2); - assert_eq!(view.node(1), None); - assert_eq!(view.node(1), None); - - let view = graph.at(1); - assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 1); - assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 1); - }); -} - -#[test] -fn test_properties() { - let graph = Graph::new(); - let props = [("test", "test")]; - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 1, props, None, None).unwrap(); - - // FIXME: Node add without properties not showing up (Issue #46) - test_graph(&graph, |graph| { - let v1 = graph.node(1).unwrap(); - let v1_w = graph.window(0, 1).node(1).unwrap(); +#[cfg(all(test, feature = "test-utils"))] +mod test { + use raphtory::{ + db::{ + api::view::Filter, + graph::views::filter::model::{NodeViewFilterOps, ViewWrapOps}, + }, + prelude::*, + test_storage, + test_utils::test_graph, + }; + use raphtory_api::core::storage::arc_str::ArcStr; + use raphtory_core::storage::timeindex::AsTime; + use std::collections::HashMap; + + #[test] + fn test_earliest_time() { + let graph = Graph::new(); + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + + // FIXME: Node add without properties not showing up (Issue #46) + test_graph(&graph, |graph| { + let view = graph.before(2); + assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 0); + assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 1); + + let view = graph.before(3); + assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 0); + assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 2); + + let view = graph.after(0); + assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 1); + assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 2); + + let view = graph.after(2); + assert_eq!(view.node(1), None); + assert_eq!(view.node(1), None); + + let view = graph.at(1); + assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 1); + assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 1); + }); + } + + #[test] + fn test_properties() { + let graph = Graph::new(); + let props = [("test", "test")]; + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 1, props, None, None).unwrap(); + + // FIXME: Node add without properties not showing up (Issue #46) + test_graph(&graph, |graph| { + let v1 = graph.node(1).unwrap(); + let v1_w = graph.window(0, 1).node(1).unwrap(); + assert_eq!( + v1.properties().as_map(), + [(ArcStr::from("test"), Prop::str("test"))] + .into_iter() + .collect::>() + ); + assert_eq!(v1_w.properties().as_map(), HashMap::default()) + }); + } + + #[test] + fn test_property_additions() { + let graph = Graph::new(); + let props = [("test", "test")]; + let v1 = graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + v1.add_updates(2, props, None).unwrap(); + let v1_w = v1.window(0, 1); assert_eq!( v1.properties().as_map(), - [(ArcStr::from("test"), Prop::str("test"))] + props .into_iter() + .map(|(k, v)| (ArcStr::from(k), v.into_prop())) .collect::>() ); assert_eq!(v1_w.properties().as_map(), HashMap::default()) - }); -} - -#[test] -fn test_property_additions() { - let graph = Graph::new(); - let props = [("test", "test")]; - let v1 = graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - v1.add_updates(2, props, None).unwrap(); - let v1_w = v1.window(0, 1); - assert_eq!( - v1.properties().as_map(), - props - .into_iter() - .map(|(k, v)| (ArcStr::from(k), v.into_prop())) - .collect::>() - ); - assert_eq!(v1_w.properties().as_map(), HashMap::default()) -} - -#[test] -fn test_metadata_additions() { - let g = Graph::new(); - let v1 = g.add_node(0, 1, NO_PROPS, None, None).unwrap(); - v1.add_metadata([("test", "test")]).unwrap(); - assert_eq!(v1.metadata().get("test"), Some("test".into())) -} - -#[test] -fn test_metadata_updates() { - let g = Graph::new(); - let v1 = g.add_node(0, 1, NO_PROPS, None, None).unwrap(); - v1.add_metadata([("test", "test")]).unwrap(); - v1.update_metadata([("test", "test2")]).unwrap(); - assert_eq!(v1.metadata().get("test"), Some("test2".into())) -} - -#[test] -#[ignore] // likely we don't want to handle it globally like this anymore, maybe we should introduce an explicit categorical property type? -fn test_string_deduplication() { - let g = Graph::new(); - let v1 = g - .add_node(0, 1, [("test1", "test"), ("test2", "test")], None, None) - .unwrap(); - let s1 = v1.properties().get("test1").unwrap_str(); - let s2 = v1.properties().get("test2").unwrap_str(); - - assert_eq!(s1.as_ptr(), s2.as_ptr()) -} - -#[test] -fn test_edge_history_and_timestamps() { - let graph = Graph::new(); - - // Add nodes - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - - // Add edges at different times - graph.add_edge(10, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(20, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(30, 2, 1, NO_PROPS, None).unwrap(); - graph.add_edge(5, 1, 3, NO_PROPS, None).unwrap(); // Earlier edge to same pair - - test_storage!(&graph, |graph| { + } + + #[test] + fn test_metadata_additions() { + let g = Graph::new(); + let v1 = g.add_node(0, 1, NO_PROPS, None, None).unwrap(); + v1.add_metadata([("test", "test")]).unwrap(); + assert_eq!(v1.metadata().get("test"), Some("test".into())) + } + + #[test] + fn test_metadata_updates() { + let g = Graph::new(); + let v1 = g.add_node(0, 1, NO_PROPS, None, None).unwrap(); + v1.add_metadata([("test", "test")]).unwrap(); + v1.update_metadata([("test", "test2")]).unwrap(); + assert_eq!(v1.metadata().get("test"), Some("test2".into())) + } + + #[test] + #[ignore] // likely we don't want to handle it globally like this anymore, maybe we should introduce an explicit categorical property type? + fn test_string_deduplication() { + let g = Graph::new(); + let v1 = g + .add_node(0, 1, [("test1", "test"), ("test2", "test")], None, None) + .unwrap(); + let s1 = v1.properties().get("test1").unwrap_str(); + let s2 = v1.properties().get("test2").unwrap_str(); + + assert_eq!(s1.as_ptr(), s2.as_ptr()) + } + + #[test] + fn test_edge_history_and_timestamps() { + let graph = Graph::new(); + + // Add nodes + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + + // Add edges at different times + graph.add_edge(10, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(20, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(30, 2, 1, NO_PROPS, None).unwrap(); + graph.add_edge(5, 1, 3, NO_PROPS, None).unwrap(); // Earlier edge to same pair + + test_storage!(&graph, |graph| { + let node1 = graph.node(1).unwrap(); + + // Test edge_history (chronological order) + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![5, 10, 20, 30]); + + // Test edge_history_rev (reverse chronological order) + let history_rev: Vec<_> = node1.edge_history_rev().map(|(t, _)| t.t()).collect(); + assert_eq!(history_rev, vec![30, 20, 10, 5]); + + // Test earliest_edge_time + assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); + + // Test latest_edge_time + assert_eq!(node1.latest_edge_time().unwrap().t(), 30); + }); + } + + #[test] + fn test_edge_timestamps_with_windows() { + let graph = Graph::new(); + + // Add nodes + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + + // Add edges at different times + graph.add_edge(5, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(15, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(25, 2, 1, NO_PROPS, None).unwrap(); + graph.add_edge(35, 1, 3, NO_PROPS, None).unwrap(); + + test_graph(&graph, |graph| { + // Test window 0-20 + let windowed = graph.window(0, 20); + let node1 = windowed.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![5, 15]); + + assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); + assert_eq!(node1.latest_edge_time().unwrap().t(), 15); + + // Test window 10-30 + let windowed = graph.window(10, 30); + let node1 = windowed.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![15, 25]); + + assert_eq!(node1.earliest_edge_time().unwrap().t(), 15); + assert_eq!(node1.latest_edge_time().unwrap().t(), 25); + + // Test window after all edges + let windowed = graph.after(40); + assert_eq!(windowed.node(1), None); // Node has no edges in this window + }); + } + + #[test] + fn test_edge_timestamps_with_layers() { + let graph = Graph::new(); + + // Add nodes + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + + // Add edges on different layers + graph.add_edge(10, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(20, 1, 3, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(30, 2, 1, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(5, 1, 3, NO_PROPS, Some("layer2")).unwrap(); + + // Test all layers let node1 = graph.node(1).unwrap(); - - // Test edge_history (chronological order) let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); assert_eq!(history, vec![5, 10, 20, 30]); - - // Test edge_history_rev (reverse chronological order) - let history_rev: Vec<_> = node1.edge_history_rev().map(|(t, _)| t.t()).collect(); - assert_eq!(history_rev, vec![30, 20, 10, 5]); - - // Test earliest_edge_time assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); - - // Test latest_edge_time assert_eq!(node1.latest_edge_time().unwrap().t(), 30); - }); -} - -#[test] -fn test_edge_timestamps_with_windows() { - let graph = Graph::new(); - - // Add nodes - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - - // Add edges at different times - graph.add_edge(5, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(15, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(25, 2, 1, NO_PROPS, None).unwrap(); - graph.add_edge(35, 1, 3, NO_PROPS, None).unwrap(); - - test_graph(&graph, |graph| { - // Test window 0-20 - let windowed = graph.window(0, 20); - let node1 = windowed.node(1).unwrap(); - - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![5, 15]); - - assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); - assert_eq!(node1.latest_edge_time().unwrap().t(), 15); - - // Test window 10-30 - let windowed = graph.window(10, 30); - let node1 = windowed.node(1).unwrap(); - - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![15, 25]); - - assert_eq!(node1.earliest_edge_time().unwrap().t(), 15); - assert_eq!(node1.latest_edge_time().unwrap().t(), 25); - - // Test window after all edges - let windowed = graph.after(40); - assert_eq!(windowed.node(1), None); // Node has no edges in this window - }); -} - -#[test] -fn test_edge_timestamps_with_layers() { - let graph = Graph::new(); - - // Add nodes - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - - // Add edges on different layers - graph.add_edge(10, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(20, 1, 3, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(30, 2, 1, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(5, 1, 3, NO_PROPS, Some("layer2")).unwrap(); - - // Test all layers - let node1 = graph.node(1).unwrap(); - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![5, 10, 20, 30]); - assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); - assert_eq!(node1.latest_edge_time().unwrap().t(), 30); - - // Test layer1 only - let layer1_graph = graph.layers(vec!["layer1"]).unwrap(); - let node1_layer1 = layer1_graph.node(1).unwrap(); - let history: Vec<_> = node1_layer1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![10, 30]); - assert_eq!(node1_layer1.earliest_edge_time().unwrap().t(), 10); - assert_eq!(node1_layer1.latest_edge_time().unwrap().t(), 30); - - // Test layer2 only - let layer2_graph = graph.layers(vec!["layer2"]).unwrap(); - let node1_layer2 = layer2_graph.node(1).unwrap(); - let history: Vec<_> = node1_layer2.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![5, 20]); - assert_eq!(node1_layer2.earliest_edge_time().unwrap().t(), 5); - assert_eq!(node1_layer2.latest_edge_time().unwrap().t(), 20); -} - -#[test] -fn test_edge_timestamps_overlapping_windows_and_layers() { - let graph = Graph::new(); - - // Add nodes - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 4, NO_PROPS, None, None).unwrap(); - - // Add edges with overlapping time ranges across multiple layers - graph.add_edge(5, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(10, 1, 3, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(15, 1, 4, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(20, 2, 1, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(25, 3, 1, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(30, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(35, 1, 4, NO_PROPS, Some("layer2")).unwrap(); - - test_graph(&graph, |graph| { - // Test overlapping window (8-22) with layer1 - let windowed_layer1 = graph.window(8, 22).layers(vec!["layer1"]).unwrap(); - let node1 = windowed_layer1.node(1).unwrap(); - - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![10, 20]); - assert_eq!(node1.earliest_edge_time().unwrap().t(), 10); - assert_eq!(node1.latest_edge_time().unwrap().t(), 20); - - // Test overlapping window (12-28) with layer2 - let windowed_layer2 = graph.window(12, 28).layers(vec!["layer2"]).unwrap(); - let node1 = windowed_layer2.node(1).unwrap(); - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![15, 25]); - assert_eq!(node1.earliest_edge_time().unwrap().t(), 15); - assert_eq!(node1.latest_edge_time().unwrap().t(), 25); - - // Test overlapping window (18-32) with both layers - let windowed_both = graph - .window(18, 32) - .layers(vec!["layer1", "layer2"]) + // Test layer1 only + let layer1_graph = graph.layers(vec!["layer1"]).unwrap(); + let node1_layer1 = layer1_graph.node(1).unwrap(); + let history: Vec<_> = node1_layer1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![10, 30]); + assert_eq!(node1_layer1.earliest_edge_time().unwrap().t(), 10); + assert_eq!(node1_layer1.latest_edge_time().unwrap().t(), 30); + + // Test layer2 only + let layer2_graph = graph.layers(vec!["layer2"]).unwrap(); + let node1_layer2 = layer2_graph.node(1).unwrap(); + let history: Vec<_> = node1_layer2.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![5, 20]); + assert_eq!(node1_layer2.earliest_edge_time().unwrap().t(), 5); + assert_eq!(node1_layer2.latest_edge_time().unwrap().t(), 20); + } + + #[test] + fn test_edge_timestamps_overlapping_windows_and_layers() { + let graph = Graph::new(); + + // Add nodes + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 4, NO_PROPS, None, None).unwrap(); + + // Add edges with overlapping time ranges across multiple layers + graph.add_edge(5, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(10, 1, 3, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(15, 1, 4, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(20, 2, 1, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(25, 3, 1, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(30, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(35, 1, 4, NO_PROPS, Some("layer2")).unwrap(); + + test_graph(&graph, |graph| { + // Test overlapping window (8-22) with layer1 + let windowed_layer1 = graph.window(8, 22).layers(vec!["layer1"]).unwrap(); + let node1 = windowed_layer1.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![10, 20]); + assert_eq!(node1.earliest_edge_time().unwrap().t(), 10); + assert_eq!(node1.latest_edge_time().unwrap().t(), 20); + + // Test overlapping window (12-28) with layer2 + let windowed_layer2 = graph.window(12, 28).layers(vec!["layer2"]).unwrap(); + let node1 = windowed_layer2.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![15, 25]); + assert_eq!(node1.earliest_edge_time().unwrap().t(), 15); + assert_eq!(node1.latest_edge_time().unwrap().t(), 25); + + // Test overlapping window (18-32) with both layers + let windowed_both = graph + .window(18, 32) + .layers(vec!["layer1", "layer2"]) + .unwrap(); + let node1 = windowed_both.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![20, 25, 30]); + assert_eq!(node1.earliest_edge_time().unwrap().t(), 20); + assert_eq!(node1.latest_edge_time().unwrap().t(), 30); + + // Test edge case: window with no edges for the node + let empty_window = graph.window(50, 60); + assert_eq!(empty_window.node(1), None); + }); + } + + #[test] + fn test_edge_timestamps_no_edges() { + let graph = Graph::new(); + + // Add a node but no edges + graph.add_node(10, 1, NO_PROPS, None, None).unwrap(); + + test_graph(&graph, |graph| { + let node1 = graph.node(1).unwrap(); + + // Node exists but has no edges + assert_eq!(node1.edge_history().count(), 0); + assert_eq!(node1.edge_history_rev().count(), 0); + assert_eq!(node1.earliest_edge_time(), None); + assert_eq!(node1.latest_edge_time(), None); + }); + } + + #[test] + fn test_node_layers() { + let graph = Graph::new(); + graph + .add_node(0, 1, [("a", "test")], None, Some("fire_nation")) + .unwrap(); + graph + .add_node(0, 2, NO_PROPS, None, Some("fire_nation")) + .unwrap(); + graph + .add_node(0, 3, [("b", 3)], None, Some("air_nomads")) .unwrap(); - let node1 = windowed_both.node(1).unwrap(); - - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![20, 25, 30]); - assert_eq!(node1.earliest_edge_time().unwrap().t(), 20); - assert_eq!(node1.latest_edge_time().unwrap().t(), 30); - - // Test edge case: window with no edges for the node - let empty_window = graph.window(50, 60); - assert_eq!(empty_window.node(1), None); - }); -} - -#[test] -fn test_edge_timestamps_no_edges() { - let graph = Graph::new(); - - // Add a node but no edges - graph.add_node(10, 1, NO_PROPS, None, None).unwrap(); - - test_graph(&graph, |graph| { - let node1 = graph.node(1).unwrap(); - // Node exists but has no edges - assert_eq!(node1.edge_history().count(), 0); - assert_eq!(node1.edge_history_rev().count(), 0); - assert_eq!(node1.earliest_edge_time(), None); - assert_eq!(node1.latest_edge_time(), None); - }); -} + let filter_expr = NodeFilter.layer("fire_nation").is_active(); + let ids = graph + .filter(filter_expr) + .unwrap() + .nodes() + .iter() + .map(|n| n.name()) + .collect::>(); + + assert_eq!(ids, vec!["1", "2"]); + } + + /// Nodes added without a layer (`layer=None`) go into STATIC_GRAPH_LAYER_ID and must be + /// visible as active in every layer-restricted view. + #[test] + fn test_unlayered_node_visible_in_all_layer_views() { + let graph = Graph::new(); + // Node 1 added without a layer — should appear in any layer view + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + // Node 2 added with "fire_nation" — should only appear in that view + graph + .add_node(0, 2, NO_PROPS, None, Some("fire_nation")) + .unwrap(); -#[test] -fn test_node_layers() { - let graph = Graph::new(); - graph - .add_node(0, 1, [("a", "test")], None, Some("fire_nation")) - .unwrap(); - graph - .add_node(0, 2, NO_PROPS, None, Some("fire_nation")) - .unwrap(); - graph - .add_node(0, 3, [("b", 3)], None, Some("air_nomads")) - .unwrap(); - - let filter_expr = NodeFilter.layer("fire_nation").is_active(); - let ids = graph - .filter(filter_expr) - .unwrap() - .nodes() - .iter() - .map(|n| n.name()) - .collect::>(); - - assert_eq!(ids, vec!["1", "2"]); -} + let fire_view = graph + .filter(NodeFilter.layer("fire_nation").is_active()) + .unwrap(); + let mut ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); + ids.sort(); + // Both node 1 (unlayered/static) and node 2 (fire_nation) should be visible + assert_eq!( + ids, + vec!["1", "2"], + "unlayered node must appear in fire_nation view" + ); + } + + /// A node added with layer A must NOT appear in a filter restricted to layer B. + #[test] + fn test_layered_node_not_visible_in_other_layer_view() { + let graph = Graph::new(); + graph + .add_node(0, 1, NO_PROPS, None, Some("fire_nation")) + .unwrap(); + graph + .add_node(0, 2, NO_PROPS, None, Some("air_nomads")) + .unwrap(); -/// Nodes added without a layer (`layer=None`) go into STATIC_GRAPH_LAYER_ID and must be -/// visible as active in every layer-restricted view. -#[test] -fn test_unlayered_node_visible_in_all_layer_views() { - let graph = Graph::new(); - // Node 1 added without a layer — should appear in any layer view - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - // Node 2 added with "fire_nation" — should only appear in that view - graph - .add_node(0, 2, NO_PROPS, None, Some("fire_nation")) - .unwrap(); - - let fire_view = graph - .filter(NodeFilter.layer("fire_nation").is_active()) - .unwrap(); - let mut ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); - ids.sort(); - // Both node 1 (unlayered/static) and node 2 (fire_nation) should be visible - assert_eq!( - ids, - vec!["1", "2"], - "unlayered node must appear in fire_nation view" - ); -} + let fire_view = graph + .filter(NodeFilter.layer("fire_nation").is_active()) + .unwrap(); + let ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); + assert_eq!( + ids, + vec!["1"], + "air_nomads node must not appear in fire_nation view" + ); + } + + /// Nodes added without a layer should appear in a windowed + layer-filtered view + /// as long as they have activity within the window. + #[test] + fn test_unlayered_node_visible_in_windowed_layer_view() { + let graph = Graph::new(); + graph.add_node(5, 1, NO_PROPS, None, None).unwrap(); // unlayered, t=5 + graph + .add_node(5, 2, NO_PROPS, None, Some("fire_nation")) + .unwrap(); // layered, t=5 + graph + .add_node(5, 3, NO_PROPS, None, Some("air_nomads")) + .unwrap(); -/// A node added with layer A must NOT appear in a filter restricted to layer B. -#[test] -fn test_layered_node_not_visible_in_other_layer_view() { - let graph = Graph::new(); - graph - .add_node(0, 1, NO_PROPS, None, Some("fire_nation")) - .unwrap(); - graph - .add_node(0, 2, NO_PROPS, None, Some("air_nomads")) - .unwrap(); - - let fire_view = graph - .filter(NodeFilter.layer("fire_nation").is_active()) - .unwrap(); - let ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); - assert_eq!( - ids, - vec!["1"], - "air_nomads node must not appear in fire_nation view" - ); -} + // Window [0, 10) covers t=5; filter to fire_nation + let view = graph.window(0, 10); + let fire_view = view + .filter(NodeFilter.layer("fire_nation").is_active()) + .unwrap(); + let mut ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); + ids.sort(); + assert_eq!( + ids, + vec!["1", "2"], + "unlayered node at t=5 must appear in windowed fire_nation view" + ); -/// Nodes added without a layer should appear in a windowed + layer-filtered view -/// as long as they have activity within the window. -#[test] -fn test_unlayered_node_visible_in_windowed_layer_view() { - let graph = Graph::new(); - graph.add_node(5, 1, NO_PROPS, None, None).unwrap(); // unlayered, t=5 - graph - .add_node(5, 2, NO_PROPS, None, Some("fire_nation")) - .unwrap(); // layered, t=5 - graph - .add_node(5, 3, NO_PROPS, None, Some("air_nomads")) - .unwrap(); - - // Window [0, 10) covers t=5; filter to fire_nation - let view = graph.window(0, 10); - let fire_view = view - .filter(NodeFilter.layer("fire_nation").is_active()) - .unwrap(); - let mut ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); - ids.sort(); - assert_eq!( - ids, - vec!["1", "2"], - "unlayered node at t=5 must appear in windowed fire_nation view" - ); - - // Window [10, 20) does NOT cover t=5, so no nodes should be active - let view_out = graph.window(10, 20); - let fire_view_out = view_out - .filter(NodeFilter.layer("fire_nation").is_active()) - .unwrap(); - assert_eq!( - fire_view_out.count_nodes(), - 0, - "no nodes should be active outside the window" - ); -} + // Window [10, 20) does NOT cover t=5, so no nodes should be active + let view_out = graph.window(10, 20); + let fire_view_out = view_out + .filter(NodeFilter.layer("fire_nation").is_active()) + .unwrap(); + assert_eq!( + fire_view_out.count_nodes(), + 0, + "no nodes should be active outside the window" + ); + } + + /// Node history (timestamps) should be scoped to the requested layer, not bleed + /// across layers. + #[test] + fn test_node_history_scoped_to_layer() { + let graph = Graph::new(); + // Node 1 exists in fire_nation at t=1 and air_nomads at t=2 + graph + .add_node(1, 1, NO_PROPS, None, Some("fire_nation")) + .unwrap(); + graph + .add_node(2, 1, NO_PROPS, None, Some("air_nomads")) + .unwrap(); -/// Node history (timestamps) should be scoped to the requested layer, not bleed -/// across layers. -#[test] -fn test_node_history_scoped_to_layer() { - let graph = Graph::new(); - // Node 1 exists in fire_nation at t=1 and air_nomads at t=2 - graph - .add_node(1, 1, NO_PROPS, None, Some("fire_nation")) - .unwrap(); - graph - .add_node(2, 1, NO_PROPS, None, Some("air_nomads")) - .unwrap(); - - let fire_layer = graph.layers("fire_nation").unwrap(); - let node = fire_layer.node(1).unwrap(); - let history: Vec = node.history().t().collect(); - // Only the fire_nation timestamp should be visible - assert_eq!( - history, - vec![1], - "history must not include timestamps from other layers" - ); + let fire_layer = graph.layers("fire_nation").unwrap(); + let node = fire_layer.node(1).unwrap(); + let history: Vec = node.history().t().collect(); + // Only the fire_nation timestamp should be visible + assert_eq!( + history, + vec![1], + "history must not include timestamps from other layers" + ); + } } diff --git a/raphtory/tests/proto_test.rs b/raphtory/tests/proto_test.rs index 730d69b42e..55514816c4 100644 --- a/raphtory/tests/proto_test.rs +++ b/raphtory/tests/proto_test.rs @@ -1,5 +1,4 @@ -#[cfg(test)] -#[cfg(feature = "proto")] +#[cfg(all(test, feature = "test-utils", feature = "proto"))] mod proto_test { use chrono::{DateTime, NaiveDateTime}; use itertools::Itertools; diff --git a/raphtory/tests/serialise_test.rs b/raphtory/tests/serialise_test.rs index 13a4faea94..5e69d1fdc1 100644 --- a/raphtory/tests/serialise_test.rs +++ b/raphtory/tests/serialise_test.rs @@ -1,5 +1,4 @@ -#[cfg(test)] -#[cfg(feature = "proto")] +#[cfg(all(test, feature = "test-utils", feature = "proto"))] mod serialise_test { use arrow::{array::types::Int32Type, datatypes::UInt8Type}; diff --git a/raphtory/tests/subgraph_tests.rs b/raphtory/tests/subgraph_tests.rs index 40830931c2..8ffc1b975f 100644 --- a/raphtory/tests/subgraph_tests.rs +++ b/raphtory/tests/subgraph_tests.rs @@ -1,563 +1,568 @@ -use ahash::HashSet; -use itertools::Itertools; -use proptest::{proptest, sample::subsequence}; -use raphtory::{ - algorithms::{components::weakly_connected_components, motifs::triangle_count::triangle_count}, - db::graph::{graph::assert_graph_equal, views::deletion_graph::PersistentGraph}, - prelude::*, - test_storage, - test_utils::{build_graph, build_graph_strat}, -}; -use raphtory_storage::mutation::addition_ops::InternalAdditionOps; -use serde_json::json; -use std::collections::BTreeSet; - -#[test] -fn test_materialize_no_edges() { - let graph = Graph::new(); - - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 2, NO_PROPS, None, None).unwrap(); - - test_storage!(&graph, |graph| { - let sg = graph.subgraph([1, 2, 1]); // <- duplicated nodes should have no effect - - let actual = sg.materialize().unwrap().into_events().unwrap(); - assert_graph_equal(&actual, &sg); - }); -} - -#[test] -fn test_remove_degree1_triangle_count() { - let graph = Graph::new(); - let edges = vec![ - (1, 2, 1), - (1, 3, 2), - (1, 4, 3), - (3, 1, 4), - (3, 4, 5), - (3, 5, 6), - (4, 5, 7), - (5, 6, 8), - (5, 8, 9), - (7, 5, 10), - (8, 5, 11), - (1, 9, 12), - (9, 1, 13), - (6, 3, 14), - (4, 8, 15), - (8, 3, 16), - (5, 10, 17), - (10, 5, 18), - (10, 8, 19), - (1, 11, 20), - (11, 1, 21), - (9, 11, 22), - (11, 9, 23), - ]; - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let subgraph = graph.subgraph(graph.nodes().into_iter().filter(|v| v.degree() > 1)); - let ts = triangle_count(&subgraph, None); - let tg = triangle_count(graph, None); - assert_eq!(ts, tg) - }); -} - -#[test] -fn layer_materialize() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(0, 3, 4, NO_PROPS, Some("2")).unwrap(); - - test_storage!(&graph, |graph| { - let sg = graph.subgraph([1, 2]); - let sgm = sg.materialize().unwrap(); - assert_eq!( - sg.unique_layers().collect_vec(), - sgm.unique_layers().collect_vec() - ); - }); -} - -#[test] -fn test_cc() { - let graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); - graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("1")).unwrap(); - let sg = graph.subgraph([0, 1, 3, 4]); - let cc = weakly_connected_components(&sg); - let groups = cc.groups(); - let group_sets = groups - .iter() - .map(|(_, g)| { - g.iter() - .map(|node| node.id()) - .sorted() - .collect::>() - }) - .collect::>(); - assert_eq!( - group_sets, - HashSet::from_iter([ - BTreeSet::from([GID::U64(0), GID::U64(1)]), - BTreeSet::from([GID::U64(3), GID::U64(4)]) - ]) - ); -} - -#[test] -fn test_layer_edges() { - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(1, 0, 1, NO_PROPS, Some("2")).unwrap(); - - assert_eq!( - graph.subgraph([0, 1]).edges().id().collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); - assert_eq!( - graph - .subgraph([0, 1]) - .valid_layers("1") - .edges() - .id() - .collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); -} - -pub mod test_filters_node_subgraph { +#[cfg(all(test, feature = "test-utils"))] +mod test { + use ahash::HashSet; + use itertools::Itertools; + use proptest::{proptest, sample::subsequence}; use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::GraphTransformer, - views::{node_subgraph::NodeSubgraph, window_graph::WindowedGraph}, - }, + algorithms::{ + components::weakly_connected_components, motifs::triangle_count::triangle_count, }, - prelude::{GraphViewOps, NodeViewOps, TimeOps}, + db::graph::{graph::assert_graph_equal, views::deletion_graph::PersistentGraph}, + prelude::*, + test_storage, + test_utils::{build_graph, build_graph_strat}, }; - use std::ops::Range; - - struct NodeSubgraphTransformer(Option>); - - impl GraphTransformer for NodeSubgraphTransformer { - type Return = NodeSubgraph; - fn apply(&self, graph: G) -> Self::Return { - let node_names: Vec = self - .0 - .clone() - .unwrap_or_else(|| graph.nodes().name().collect::>()); - graph.subgraph(node_names) - } - } + use raphtory_storage::mutation::addition_ops::InternalAdditionOps; + use serde_json::json; + use std::collections::BTreeSet; - struct WindowedNodeSubgraphTransformer(Option>, Range); - - impl GraphTransformer for WindowedNodeSubgraphTransformer { - type Return = NodeSubgraph>; - fn apply(&self, graph: G) -> Self::Return { - let graph = graph.window(self.1.start, self.1.end); - let node_names: Vec = self - .0 - .clone() - .unwrap_or_else(|| graph.nodes().name().collect::>()); - graph.subgraph(node_names) - } - } + #[test] + fn test_materialize_no_edges() { + let graph = Graph::new(); - mod test_nodes_filters_node_subgraph { - use crate::test_filters_node_subgraph::{ - NodeSubgraphTransformer, WindowedNodeSubgraphTransformer, - }; - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - TestGraphVariants, TestVariants, - }, - views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, - }, - }, - prelude::{AdditionOps, NodeFilter}, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - fn init_graph(graph: G) -> G { - let nodes = vec![ - (6, "N1", vec![("p1", Prop::U64(2u64))]), - (7, "N1", vec![("p1", Prop::U64(1u64))]), - (6, "N2", vec![("p1", Prop::U64(1u64))]), - (7, "N2", vec![("p1", Prop::U64(2u64))]), - (8, "N3", vec![("p1", Prop::U64(1u64))]), - (9, "N4", vec![("p1", Prop::U64(1u64))]), - (5, "N5", vec![("p1", Prop::U64(1u64))]), - (6, "N5", vec![("p1", Prop::U64(2u64))]), - (5, "N6", vec![("p1", Prop::U64(1u64))]), - (6, "N6", vec![("p1", Prop::U64(1u64))]), - (3, "N7", vec![("p1", Prop::U64(1u64))]), - (5, "N7", vec![("p1", Prop::U64(1u64))]), - (3, "N8", vec![("p1", Prop::U64(1u64))]), - (4, "N8", vec![("p1", Prop::U64(2u64))]), - ]; - - for (id, name, props) in &nodes { - graph - .add_node(*id, name, props.clone(), None, None) - .unwrap(); - } + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 2, NO_PROPS, None, None).unwrap(); - graph - } + test_storage!(&graph, |graph| { + let sg = graph.subgraph([1, 2, 1]); // <- duplicated nodes should have no effect - #[test] - fn test_search_nodes_subgraph() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = ["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - NodeSubgraphTransformer(None), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - NodeSubgraphTransformer(None), - filter, - &expected_results, - TestVariants::All, - ); + let actual = sg.materialize().unwrap().into_events().unwrap(); + assert_graph_equal(&actual, &sg); + }); + } - let node_names: Option> = - Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N3", "N4"]; - assert_filter_nodes_results( - init_graph, - NodeSubgraphTransformer(node_names.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - NodeSubgraphTransformer(node_names), - filter, - &expected_results, - TestVariants::All, - ); + #[test] + fn test_remove_degree1_triangle_count() { + let graph = Graph::new(); + let edges = vec![ + (1, 2, 1), + (1, 3, 2), + (1, 4, 3), + (3, 1, 4), + (3, 4, 5), + (3, 5, 6), + (4, 5, 7), + (5, 6, 8), + (5, 8, 9), + (7, 5, 10), + (8, 5, 11), + (1, 9, 12), + (9, 1, 13), + (6, 3, 14), + (4, 8, 15), + (8, 3, 16), + (5, 10, 17), + (10, 5, 18), + (10, 8, 19), + (1, 11, 20), + (11, 1, 21), + (9, 11, 22), + (11, 9, 23), + ]; + for (src, dst, ts) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); } + test_storage!(&graph, |graph| { + let subgraph = graph.subgraph(graph.nodes().into_iter().filter(|v| v.degree() > 1)); + let ts = triangle_count(&subgraph, None); + let tg = triangle_count(graph, None); + assert_eq!(ts, tg) + }); + } - #[test] - fn test_search_nodes_subgraph_w() { - // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], + #[test] + fn layer_materialize() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(0, 3, 4, NO_PROPS, Some("2")).unwrap(); + + test_storage!(&graph, |graph| { + let sg = graph.subgraph([1, 2]); + let sgm = sg.materialize().unwrap(); + assert_eq!( + sg.unique_layers().collect_vec(), + sgm.unique_layers().collect_vec() ); + }); + } - let node_names: Option> = Some(vec!["N3".into()]); - let filter = NodeFilter.property("p1").gt(0u64); - let expected_results = vec!["N3"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } + #[test] + fn test_cc() { + let graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); + graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("1")).unwrap(); + let sg = graph.subgraph([0, 1, 3, 4]); + let cc = weakly_connected_components(&sg); + let groups = cc.groups(); + let group_sets = groups + .iter() + .map(|(_, g)| { + g.iter() + .map(|node| node.id()) + .sorted() + .collect::>() + }) + .collect::>(); + assert_eq!( + group_sets, + HashSet::from_iter([ + BTreeSet::from([GID::U64(0), GID::U64(1)]), + BTreeSet::from([GID::U64(3), GID::U64(4)]) + ]) + ); + } - #[test] - fn test_search_nodes_pg_w() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_layer_edges() { + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(1, 0, 1, NO_PROPS, Some("2")).unwrap(); - let node_names: Option> = - Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); - let filter = NodeFilter.property("p1").ge(1u64); - let expected_results = vec!["N2", "N3", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + assert_eq!( + graph.subgraph([0, 1]).edges().id().collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); + assert_eq!( + graph + .subgraph([0, 1]) + .valid_layers("1") + .edges() + .id() + .collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); } - mod test_edges_filters_node_subgraph { + pub mod test_filters_node_subgraph { use raphtory::{ db::{ api::view::StaticGraphViewOps, graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestVariants, - }, - views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, + assertions::GraphTransformer, + views::{node_subgraph::NodeSubgraph, window_graph::WindowedGraph}, }, }, - prelude::{AdditionOps, EdgeFilter}, + prelude::{GraphViewOps, NodeViewOps, TimeOps}, }; - use raphtory_api::core::entities::properties::prop::Prop; + use std::ops::Range; + + struct NodeSubgraphTransformer(Option>); + + impl GraphTransformer for NodeSubgraphTransformer { + type Return = NodeSubgraph; + fn apply(&self, graph: G) -> Self::Return { + let node_names: Vec = self + .0 + .clone() + .unwrap_or_else(|| graph.nodes().name().collect::>()); + graph.subgraph(node_names) + } + } - use crate::test_filters_node_subgraph::{ - NodeSubgraphTransformer, WindowedNodeSubgraphTransformer, - }; + struct WindowedNodeSubgraphTransformer(Option>, Range); + + impl GraphTransformer for WindowedNodeSubgraphTransformer { + type Return = NodeSubgraph>; + fn apply(&self, graph: G) -> Self::Return { + let graph = graph.window(self.1.start, self.1.end); + let node_names: Vec = self + .0 + .clone() + .unwrap_or_else(|| graph.nodes().name().collect::>()); + graph.subgraph(node_names) + } + } + + mod test_nodes_filters_node_subgraph { + use crate::test::test_filters_node_subgraph::{ + NodeSubgraphTransformer, WindowedNodeSubgraphTransformer, + }; + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::{ + assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, + TestGraphVariants, TestVariants, + }, + views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }, + }, + }, + prelude::{AdditionOps, NodeFilter}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + fn init_graph(graph: G) -> G { + let nodes = vec![ + (6, "N1", vec![("p1", Prop::U64(2u64))]), + (7, "N1", vec![("p1", Prop::U64(1u64))]), + (6, "N2", vec![("p1", Prop::U64(1u64))]), + (7, "N2", vec![("p1", Prop::U64(2u64))]), + (8, "N3", vec![("p1", Prop::U64(1u64))]), + (9, "N4", vec![("p1", Prop::U64(1u64))]), + (5, "N5", vec![("p1", Prop::U64(1u64))]), + (6, "N5", vec![("p1", Prop::U64(2u64))]), + (5, "N6", vec![("p1", Prop::U64(1u64))]), + (6, "N6", vec![("p1", Prop::U64(1u64))]), + (3, "N7", vec![("p1", Prop::U64(1u64))]), + (5, "N7", vec![("p1", Prop::U64(1u64))]), + (3, "N8", vec![("p1", Prop::U64(1u64))]), + (4, "N8", vec![("p1", Prop::U64(2u64))]), + ]; + + for (id, name, props) in &nodes { + graph + .add_node(*id, name, props.clone(), None, None) + .unwrap(); + } - fn init_graph(graph: G) -> G { - let edges = vec![ - (6, "N1", "N2", vec![("p1", Prop::U64(2u64))]), - (7, "N1", "N2", vec![("p1", Prop::U64(1u64))]), - (6, "N2", "N3", vec![("p1", Prop::U64(1u64))]), - (7, "N2", "N3", vec![("p1", Prop::U64(2u64))]), - (8, "N3", "N4", vec![("p1", Prop::U64(1u64))]), - (9, "N4", "N5", vec![("p1", Prop::U64(1u64))]), - (5, "N5", "N6", vec![("p1", Prop::U64(1u64))]), - (6, "N5", "N6", vec![("p1", Prop::U64(2u64))]), - (5, "N6", "N7", vec![("p1", Prop::U64(1u64))]), - (6, "N6", "N7", vec![("p1", Prop::U64(1u64))]), - (3, "N7", "N8", vec![("p1", Prop::U64(1u64))]), - (5, "N7", "N8", vec![("p1", Prop::U64(1u64))]), - (3, "N8", "N1", vec![("p1", Prop::U64(1u64))]), - (4, "N8", "N1", vec![("p1", Prop::U64(2u64))]), - ]; - - for (id, src, tgt, props) in &edges { - graph.add_edge(*id, src, tgt, props.clone(), None).unwrap(); + graph } - graph - } + #[test] + fn test_search_nodes_subgraph() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = ["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + NodeSubgraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + NodeSubgraphTransformer(None), + filter, + &expected_results, + TestVariants::All, + ); + + let node_names: Option> = + Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N3", "N4"]; + assert_filter_nodes_results( + init_graph, + NodeSubgraphTransformer(node_names.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + NodeSubgraphTransformer(node_names), + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_edges_filters() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - NodeSubgraphTransformer(None), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - NodeSubgraphTransformer(None), - filter, - &expected_results, - TestVariants::All, - ); + #[test] + fn test_search_nodes_subgraph_w() { + // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let node_names: Option> = Some(vec!["N3".into()]); + let filter = NodeFilter.property("p1").gt(0u64); + let expected_results = vec!["N3"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - let node_names: Option> = - Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = vec!["N3->N4", "N4->N5"]; - assert_filter_edges_results( - init_graph, - NodeSubgraphTransformer(node_names.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - NodeSubgraphTransformer(node_names), - filter, - &expected_results, - TestVariants::All, - ); + #[test] + fn test_search_nodes_pg_w() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let node_names: Option> = + Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); + let filter = NodeFilter.property("p1").ge(1u64); + let expected_results = vec!["N2", "N3", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } } - #[test] - fn test_edges_filters_w() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + mod test_edges_filters_node_subgraph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::{ + assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestVariants, + }, + views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }, + }, + }, + prelude::{AdditionOps, EdgeFilter}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + use crate::test::test_filters_node_subgraph::{ + NodeSubgraphTransformer, WindowedNodeSubgraphTransformer, + }; + + fn init_graph(graph: G) -> G { + let edges = vec![ + (6, "N1", "N2", vec![("p1", Prop::U64(2u64))]), + (7, "N1", "N2", vec![("p1", Prop::U64(1u64))]), + (6, "N2", "N3", vec![("p1", Prop::U64(1u64))]), + (7, "N2", "N3", vec![("p1", Prop::U64(2u64))]), + (8, "N3", "N4", vec![("p1", Prop::U64(1u64))]), + (9, "N4", "N5", vec![("p1", Prop::U64(1u64))]), + (5, "N5", "N6", vec![("p1", Prop::U64(1u64))]), + (6, "N5", "N6", vec![("p1", Prop::U64(2u64))]), + (5, "N6", "N7", vec![("p1", Prop::U64(1u64))]), + (6, "N6", "N7", vec![("p1", Prop::U64(1u64))]), + (3, "N7", "N8", vec![("p1", Prop::U64(1u64))]), + (5, "N7", "N8", vec![("p1", Prop::U64(1u64))]), + (3, "N8", "N1", vec![("p1", Prop::U64(1u64))]), + (4, "N8", "N1", vec![("p1", Prop::U64(2u64))]), + ]; + + for (id, src, tgt, props) in &edges { + graph.add_edge(*id, src, tgt, props.clone(), None).unwrap(); + } - let node_names: Option> = - Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); - let filter = EdgeFilter.property("p1").ge(1u64); - let expected_results = vec!["N2->N3", "N3->N4"]; - assert_filter_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + graph + } - #[test] - fn test_edges_filters_pg_w() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + NodeSubgraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + NodeSubgraphTransformer(None), + filter, + &expected_results, + TestVariants::All, + ); + + let node_names: Option> = + Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = vec!["N3->N4", "N4->N5"]; + assert_filter_edges_results( + init_graph, + NodeSubgraphTransformer(node_names.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + NodeSubgraphTransformer(node_names), + filter, + &expected_results, + TestVariants::All, + ); + } - let node_names: Option> = Some(vec![ - "N2".into(), - "N3".into(), - "N4".into(), - "N5".into(), - "N6".into(), - ]); - let filter = EdgeFilter.property("p1").lt(2u64); - let expected_results = vec!["N3->N4"]; - assert_filter_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_w() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let node_names: Option> = + Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); + let filter = EdgeFilter.property("p1").ge(1u64); + let expected_results = vec!["N2->N3", "N3->N4"]; + assert_filter_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_w() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let node_names: Option> = Some(vec![ + "N2".into(), + "N3".into(), + "N4".into(), + "N5".into(), + "N6".into(), + ]); + let filter = EdgeFilter.property("p1").lt(2u64); + let expected_results = vec!["N3->N4"]; + assert_filter_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } } } -} -#[test] -fn nodes_without_updates_are_filtered() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - let expected = Graph::new(); - expected.resolve_layer(None).unwrap(); - let subgraph = g.subgraph([0]); - assert_graph_equal(&subgraph, &expected); -} + #[test] + fn nodes_without_updates_are_filtered() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + let expected = Graph::new(); + expected.resolve_layer(None).unwrap(); + let subgraph = g.subgraph([0]); + assert_graph_equal(&subgraph, &expected); + } -#[test] -fn materialize_proptest() { - proptest!(|(graph in build_graph_strat(10, 10, 10, 10, false), nodes in subsequence((0..10).collect::>(), 0..10))| { - let graph = Graph::from(build_graph(&graph)); - let subgraph = graph.subgraph(nodes); - assert_graph_equal(&subgraph, &subgraph.materialize().unwrap()); - }) -} + #[test] + fn materialize_proptest() { + proptest!(|(graph in build_graph_strat(10, 10, 10, 10, false), nodes in subsequence((0..10).collect::>(), 0..10))| { + let graph = Graph::from(build_graph(&graph)); + let subgraph = graph.subgraph(nodes); + assert_graph_equal(&subgraph, &subgraph.materialize().unwrap()); + }) + } -#[test] -fn materialize_proptest_failure() { - let graph_f = serde_json::from_value(json!({"nodes":{},"edges":[[[1,1,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let graph = Graph::from(build_graph(&graph_f)); - let subgraph = graph.subgraph([1]); - let nodes = subgraph.default_layer().nodes().id().collect_vec(); - dbg!(nodes); - assert_eq!(subgraph.default_layer().count_nodes(), 0); - assert_eq!(subgraph.count_edges(), 1); - let materialised = subgraph.materialize().unwrap(); - assert_graph_equal(&subgraph, &materialised); -} + #[test] + fn materialize_proptest_failure() { + let graph_f = serde_json::from_value(json!({"nodes":{},"edges":[[[1,1,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let graph = Graph::from(build_graph(&graph_f)); + let subgraph = graph.subgraph([1]); + let nodes = subgraph.default_layer().nodes().id().collect_vec(); + dbg!(nodes); + assert_eq!(subgraph.default_layer().count_nodes(), 0); + assert_eq!(subgraph.count_edges(), 1); + let materialised = subgraph.materialize().unwrap(); + assert_graph_equal(&subgraph, &materialised); + } -#[test] -fn materialize_persistent_proptest() { - proptest!(|(graph in build_graph_strat(10, 10, 10, 10, true), nodes in subsequence((0..10).collect::>(), 0..10))| { - let graph = PersistentGraph::from(build_graph(&graph)); - let subgraph = graph.subgraph(nodes); - assert_graph_equal(&subgraph, &subgraph.materialize().unwrap()); - }) -} + #[test] + fn materialize_persistent_proptest() { + proptest!(|(graph in build_graph_strat(10, 10, 10, 10, true), nodes in subsequence((0..10).collect::>(), 0..10))| { + let graph = PersistentGraph::from(build_graph(&graph)); + let subgraph = graph.subgraph(nodes); + assert_graph_equal(&subgraph, &subgraph.materialize().unwrap()); + }) + } -#[test] -fn test_subgraph_only_deletion() { - let g = PersistentGraph::new(); - g.delete_edge(0, 0, 1, None).unwrap(); - let sg = g.subgraph([0]); - let expected = PersistentGraph::new(); - expected.resolve_layer(None).unwrap(); - assert_graph_equal(&sg, &expected); + #[test] + fn test_subgraph_only_deletion() { + let g = PersistentGraph::new(); + g.delete_edge(0, 0, 1, None).unwrap(); + let sg = g.subgraph([0]); + let expected = PersistentGraph::new(); + expected.resolve_layer(None).unwrap(); + assert_graph_equal(&sg, &expected); + } } diff --git a/raphtory/tests/test_deletions.rs b/raphtory/tests/test_deletions.rs index 58ff3ad0eb..7b49f7d531 100644 --- a/raphtory/tests/test_deletions.rs +++ b/raphtory/tests/test_deletions.rs @@ -1,1183 +1,1186 @@ -use itertools::Itertools; -use proptest::{arbitrary::any, proptest, sample::subsequence}; -use raphtory::test_utils::{build_graph, build_graph_strat}; -use raphtory::{ - db::graph::{ - edge::EdgeView, - graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, - views::deletion_graph::PersistentGraph, - // views::deletion_graph::{GraphTimeSemanticsOps, PersistentGraph}, - }, - prelude::*, - test_storage, -}; -use raphtory_api::core::{ - entities::GID, - storage::timeindex::{AsTime, EventTime}, -}; -use raphtory_storage::mutation::addition_ops::InternalAdditionOps; -use rayon::ThreadPoolBuilder; -use std::ops::Range; - -#[test] -fn test_nodes() { - let g = PersistentGraph::new(); - - let edges = [ - (0, 1, 2, "assigned"), - (1, 1, 3, "assigned"), - (2, 4, 2, "has"), - (3, 4, 2, "has"), - (4, 5, 2, "blocks"), - (5, 4, 5, "has"), - (6, 6, 5, "assigned"), - ]; - - for (time, src, dst, layer) in edges { - g.add_edge(time, src, dst, [("added", Prop::I64(0))], Some(layer)) - .unwrap(); - } - - let nodes = g - .window(0, 1701786285758) - .layers(vec!["assigned", "has", "blocks"]) - .unwrap() - .nodes() - .into_iter() - .map(|vv| vv.name()) - .collect_vec(); - - assert_eq!(nodes, vec!["1", "2", "3", "4", "5", "6"]); - - let nodes = g - .at(1701786285758) - .layers(vec!["assigned", "has", "blocks"]) - .unwrap() - .nodes() - .into_iter() - .map(|vv| vv.name()) - .collect_vec(); - - assert_eq!(nodes, vec!["1", "2", "3", "4", "5", "6"]); -} - -#[test] -fn test_edge_deletions() { - let g = PersistentGraph::new(); - - g.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) - .unwrap(); - g.delete_edge(10, 0, 1, None).unwrap(); - - assert_eq!( - g.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - - assert_eq!( - g.window(1, 2).edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - - assert_eq!(g.window(1, 2).count_edges(), 1); - - assert_eq!(g.window(11, 12).count_edges(), 0); - - assert_eq!( - g.window(1, 2) - .edge(0, 1) +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use proptest::{arbitrary::any, proptest, sample::subsequence}; + use raphtory::test_utils::{build_graph, build_graph_strat}; + use raphtory::{ + db::graph::{ + edge::EdgeView, + graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, + views::deletion_graph::PersistentGraph, + // views::deletion_graph::{GraphTimeSemanticsOps, PersistentGraph}, + }, + prelude::*, + test_storage, + }; + use raphtory_api::core::{ + entities::GID, + storage::timeindex::{AsTime, EventTime}, + }; + use raphtory_storage::mutation::addition_ops::InternalAdditionOps; + use rayon::ThreadPoolBuilder; + use std::ops::Range; + + #[test] + fn test_nodes() { + let g = PersistentGraph::new(); + + let edges = [ + (0, 1, 2, "assigned"), + (1, 1, 3, "assigned"), + (2, 4, 2, "has"), + (3, 4, 2, "has"), + (4, 5, 2, "blocks"), + (5, 4, 5, "has"), + (6, 6, 5, "assigned"), + ]; + + for (time, src, dst, layer) in edges { + g.add_edge(time, src, dst, [("added", Prop::I64(0))], Some(layer)) + .unwrap(); + } + + let nodes = g + .window(0, 1701786285758) + .layers(vec!["assigned", "has", "blocks"]) .unwrap() - .properties() - .get("added") - .unwrap_i64(), - 0 - ); + .nodes() + .into_iter() + .map(|vv| vv.name()) + .collect_vec(); - assert!(g.window(11, 12).edge(0, 1).is_none()); + assert_eq!(nodes, vec!["1", "2", "3", "4", "5", "6"]); - assert_eq!( - g.window(1, 2) - .edge(0, 1) + let nodes = g + .at(1701786285758) + .layers(vec!["assigned", "has", "blocks"]) .unwrap() - .properties() - .temporal() - .get("added") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - vec![(1, Prop::I64(0))] - ); - - assert_eq!(g.window(1, 2).node(0).unwrap().out_degree(), 1) -} + .nodes() + .into_iter() + .map(|vv| vv.name()) + .collect_vec(); -#[test] -fn test_window_semantics() { - let g = PersistentGraph::new(); - g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); - g.delete_edge(10, 1, 2, None).unwrap(); - - assert_eq!(g.count_edges(), 1); - - assert_eq!(g.at(12).count_edges(), 0); - assert_eq!(g.at(11).count_edges(), 0); - assert_eq!(g.at(10).count_edges(), 0); - assert_eq!(g.at(9).count_edges(), 1); - assert_eq!(g.window(5, 9).count_edges(), 1); - assert_eq!(g.window(5, 10).count_edges(), 1); - assert_eq!(g.window(5, 11).count_edges(), 1); - assert_eq!(g.window(10, 12).count_edges(), 0); - assert_eq!(g.before(10).count_edges(), 1); - assert_eq!(g.after(10).count_edges(), 0); -} - -#[test] -fn test_timestamps() { - let g = PersistentGraph::new(); - let e = g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); - assert_eq!(e.earliest_time().unwrap(), 1); // time of first addition - assert_eq!(e.latest_time().map(|t| t.t()), Some(1)); // not deleted so alive forever - g.delete_edge(10, 1, 2, None).unwrap(); - assert_eq!(e.latest_time().unwrap(), 10); // deleted, so time of last deletion - - g.delete_edge(10, 3, 4, None).unwrap(); - let e = g.edge(3, 4).unwrap(); - assert_eq!(e.earliest_time().unwrap(), 10); // only deleted, earliest and latest time are the same - assert_eq!(e.latest_time().unwrap(), 10); - g.add_edge(1, 3, 4, [("test", "test")], None).unwrap(); - assert_eq!(e.latest_time().unwrap(), 10); - assert_eq!(e.earliest_time().unwrap(), 1); // added so timestamp is now the first addition -} - -#[test] -fn test_materialize_only_deletion() { - let g = PersistentGraph::new(); - g.delete_edge(1, 1, 2, None).unwrap(); - g.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); - g.delete_edge(5, 1, 2, None).unwrap(); - g.delete_edge(10, 1, 2, None).unwrap(); - assert_eq!( - g.window(0, 11).count_temporal_edges(), - g.count_temporal_edges() - ); - assert_graph_equal(&g.materialize().unwrap(), &g); -} - -#[test] -fn materialize_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { - let g = PersistentGraph::from(build_graph(&graph_f)); - let gm = g.materialize().unwrap(); - assert_graph_equal(&g, &gm); - }) -} - -#[test] -fn materialize_window_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = PersistentGraph::from(build_graph(&graph_f)); - let gw = g.window(w.start, w.end); - let gmw = gw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gw, &gmw); - }) -} - -#[test] -fn test_multilayer_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(10, 0, 0, NO_PROPS, Some("a")).unwrap(); - println!("all: {g:?}"); - let gw = g.window(1, 10); - println!("windowed nodes: {:?}", gw.nodes()); - let gm = gw.materialize().unwrap(); - - println!("materialized: {:?}", gm); - - assert_eq!(gw.valid_layers("a").count_nodes(), 0); - assert_eq!(gm.valid_layers("a").count_nodes(), 0); - - let expected = PersistentGraph::new(); - expected.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); - expected.resolve_layer(Some("a")).unwrap(); // empty layer exists + assert_eq!(nodes, vec!["1", "2", "3", "4", "5", "6"]); + } - println!("expected: {:?}", expected); - assert_persistent_materialize_graph_equal(&gw, &expected); + #[test] + fn test_edge_deletions() { + let g = PersistentGraph::new(); - assert_persistent_materialize_graph_equal(&gw, &gm); -} + g.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) + .unwrap(); + g.delete_edge(10, 0, 1, None).unwrap(); + + assert_eq!( + g.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + + assert_eq!( + g.window(1, 2).edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + + assert_eq!(g.window(1, 2).count_edges(), 1); + + assert_eq!(g.window(11, 12).count_edges(), 0); + + assert_eq!( + g.window(1, 2) + .edge(0, 1) + .unwrap() + .properties() + .get("added") + .unwrap_i64(), + 0 + ); + + assert!(g.window(11, 12).edge(0, 1).is_none()); + + assert_eq!( + g.window(1, 2) + .edge(0, 1) + .unwrap() + .properties() + .temporal() + .get("added") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + vec![(1, Prop::I64(0))] + ); + + assert_eq!(g.window(1, 2).node(0).unwrap().out_degree(), 1) + } -#[test] -fn test_multilayer_window2() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); - let gw = g.window(1, i64::MAX); - let gm = gw.materialize().unwrap(); - println!("original: {g:?}"); - assert_eq!(g.default_layer().count_nodes(), 1); - println!("materialized: {gm:?}"); - assert_eq!(gm.default_layer().count_nodes(), 1); - - assert_persistent_materialize_graph_equal(&gw, &gm.clone().into_persistent().unwrap()); - assert_persistent_materialize_graph_equal(&gw, &gm); -} + #[test] + fn test_window_semantics() { + let g = PersistentGraph::new(); + g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); + g.delete_edge(10, 1, 2, None).unwrap(); + + assert_eq!(g.count_edges(), 1); + + assert_eq!(g.at(12).count_edges(), 0); + assert_eq!(g.at(11).count_edges(), 0); + assert_eq!(g.at(10).count_edges(), 0); + assert_eq!(g.at(9).count_edges(), 1); + assert_eq!(g.window(5, 9).count_edges(), 1); + assert_eq!(g.window(5, 10).count_edges(), 1); + assert_eq!(g.window(5, 11).count_edges(), 1); + assert_eq!(g.window(10, 12).count_edges(), 0); + assert_eq!(g.before(10).count_edges(), 1); + assert_eq!(g.after(10).count_edges(), 0); + } -#[test] -fn test_deletion_at_window_start() { - let g = PersistentGraph::new(); - g.add_edge(2, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(0, 0, 1, None).unwrap(); + #[test] + fn test_timestamps() { + let g = PersistentGraph::new(); + let e = g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); + assert_eq!(e.earliest_time().unwrap(), 1); // time of first addition + assert_eq!(e.latest_time().map(|t| t.t()), Some(1)); // not deleted so alive forever + g.delete_edge(10, 1, 2, None).unwrap(); + assert_eq!(e.latest_time().unwrap(), 10); // deleted, so time of last deletion + + g.delete_edge(10, 3, 4, None).unwrap(); + let e = g.edge(3, 4).unwrap(); + assert_eq!(e.earliest_time().unwrap(), 10); // only deleted, earliest and latest time are the same + assert_eq!(e.latest_time().unwrap(), 10); + g.add_edge(1, 3, 4, [("test", "test")], None).unwrap(); + assert_eq!(e.latest_time().unwrap(), 10); + assert_eq!(e.earliest_time().unwrap(), 1); // added so timestamp is now the first addition + } - // deletion at start of window is not part of the view - let gw = g.window(0, 1); - assert!(gw.is_empty()); + #[test] + fn test_materialize_only_deletion() { + let g = PersistentGraph::new(); + g.delete_edge(1, 1, 2, None).unwrap(); + g.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); + g.delete_edge(5, 1, 2, None).unwrap(); + g.delete_edge(10, 1, 2, None).unwrap(); + assert_eq!( + g.window(0, 11).count_temporal_edges(), + g.count_temporal_edges() + ); + assert_graph_equal(&g.materialize().unwrap(), &g); + } - let gw = g.window(0, 3); - assert_eq!(gw.node(0).unwrap().earliest_time().unwrap().t(), 2); - assert_eq!(gw.node(1).unwrap().earliest_time().unwrap().t(), 2); -} + #[test] + fn materialize_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { + let g = PersistentGraph::from(build_graph(&graph_f)); + let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); + }) + } -#[test] -fn materialize_window_layers_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), l in subsequence(&["a", "b"], 0..=2), num_threads in 1..=16usize)| { - let pool = ThreadPoolBuilder::new().num_threads(num_threads).build().unwrap(); - pool.install(|| { + #[test] + fn materialize_window_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { let g = PersistentGraph::from(build_graph(&graph_f)); - let glw = g.valid_layers(l.clone()).window(w.start, w.end); - let gmlw = glw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&glw, &gmlw); + let gw = g.window(w.start, w.end); + let gmw = gw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gw, &gmw); }) + } - }) -} + #[test] + fn test_multilayer_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(10, 0, 0, NO_PROPS, Some("a")).unwrap(); + println!("all: {g:?}"); + let gw = g.window(1, 10); + println!("windowed nodes: {:?}", gw.nodes()); + let gm = gw.materialize().unwrap(); -#[test] -fn materialize_window_multilayer() { - let g = PersistentGraph::new(); - g.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); - g.delete_edge(3, 0, 0, Some("a")).unwrap(); - let w = 0..10; - let glw = g.valid_layers("a").window(w.start, w.end); - let gmlw = glw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&glw, &gmlw); -} + println!("materialized: {:?}", gm); -#[test] -fn test_materialize_deleted_edge() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(1, 0, 1, None).unwrap(); - g.delete_edge(5, 0, 1, None).unwrap(); + assert_eq!(gw.valid_layers("a").count_nodes(), 0); + assert_eq!(gm.valid_layers("a").count_nodes(), 0); - let gw = g.window(2, 10); + let expected = PersistentGraph::new(); + expected.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); + expected.resolve_layer(Some("a")).unwrap(); // empty layer exists - let gm = gw.materialize().unwrap(); + println!("expected: {:?}", expected); + assert_persistent_materialize_graph_equal(&gw, &expected); - assert_graph_equal(&gw, &gm); -} + assert_persistent_materialize_graph_equal(&gw, &gm); + } -#[test] -fn test_addition_deletion_multilayer_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); - g.delete_edge(0, 0, 0, None).unwrap(); - let gw = g.window(0, 0).valid_layers("a"); - let expected_gw = PersistentGraph::new(); - expected_gw.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gw, &expected_gw); - let gwm = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gwm); -} + #[test] + fn test_multilayer_window2() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); + let gw = g.window(1, i64::MAX); + let gm = gw.materialize().unwrap(); + println!("original: {g:?}"); + assert_eq!(g.default_layer().count_nodes(), 1); + println!("materialized: {gm:?}"); + assert_eq!(gm.default_layer().count_nodes(), 1); + + assert_persistent_materialize_graph_equal(&gw, &gm.clone().into_persistent().unwrap()); + assert_persistent_materialize_graph_equal(&gw, &gm); + } -#[test] -fn materialize_broken_time() { - let g = PersistentGraph::new(); - g.add_edge( - -7868307470600541330, - 0, - 0, - [("test", Prop::map([("x", "y")]))], - Some("a"), - ) - .unwrap(); - g.add_edge( - -8675512464616562592, - 0, - 0, - [("test", Prop::map([("z", "hi")]))], - None, - ) - .unwrap(); - g.edge(0, 0) - .unwrap() - .add_metadata([("other", "b")], None) - .unwrap(); - let gw = g.window(-7549523977641994620, -995047120251067629); - assert_persistent_materialize_graph_equal(&gw, &gw.materialize().unwrap()) -} + #[test] + fn test_deletion_at_window_start() { + let g = PersistentGraph::new(); + g.add_edge(2, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(0, 0, 1, None).unwrap(); -#[test] -fn test_materialize_window_start_before_node_add() { - let g = PersistentGraph::new(); - g.add_node(-1, 0, [("test", "test")], None, None).unwrap(); - g.add_node(5, 0, [("test", "blob")], None, None).unwrap(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - let gw = g.window(-5, 8); - let gmw = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gmw); -} + // deletion at start of window is not part of the view + let gw = g.window(0, 1); + assert!(gw.is_empty()); -#[test] -fn test_materialize_edge_metadata() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.add_metadata([("test", "test")], None).unwrap(); - g.delete_edge(1, 1, 2, None).unwrap(); - - let gw = g.after(1); - let gmw = gw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gw, &gmw); -} + let gw = g.window(0, 3); + assert_eq!(gw.node(0).unwrap().earliest_time().unwrap().t(), 2); + assert_eq!(gw.node(1).unwrap().earliest_time().unwrap().t(), 2); + } -#[test] -fn test_metadata_multiple_layers() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.add_metadata([("test", "test")], None).unwrap(); - g.delete_edge(1, 1, 2, None).unwrap(); - assert_eq!( - g.edge(1, 2).unwrap().metadata().as_vec(), - [("test".into(), Prop::map([("_default", "test")]))] - ); - let gw = g.after(1); - assert!(gw - .edge(1, 2) - .unwrap() - .metadata() - .iter_filtered() - .next() - .is_none()); - let g_before = g.before(1); - assert_eq!( - g_before.edge(1, 2).unwrap().metadata().as_vec(), - [("test".into(), Prop::map([("_default", "test")]))] - ); -} + #[test] + fn materialize_window_layers_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), l in subsequence(&["a", "b"], 0..=2), num_threads in 1..=16usize)| { + let pool = ThreadPoolBuilder::new().num_threads(num_threads).build().unwrap(); + pool.install(|| { + let g = PersistentGraph::from(build_graph(&graph_f)); + let glw = g.valid_layers(l.clone()).window(w.start, w.end); + let gmlw = glw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&glw, &gmlw); + }) -#[test] -fn test_materialize_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.delete_edge(10, 1, 2, None).unwrap(); - - let gm = g - .window(3, 5) - .materialize() - .unwrap() - .into_persistent() - .unwrap(); - assert_persistent_materialize_graph_equal(&g.window(3, 5), &gm); // ignore start of window as it has different updates by design -} + }) + } -#[test] -fn test_materialize_window_node_props() { - let g = Graph::new(); - g.add_node(0, 1, [("test", "test")], None, None).unwrap(); + #[test] + fn materialize_window_multilayer() { + let g = PersistentGraph::new(); + g.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); + g.delete_edge(3, 0, 0, Some("a")).unwrap(); + let w = 0..10; + let glw = g.valid_layers("a").window(w.start, w.end); + let gmlw = glw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&glw, &gmlw); + } - test_storage!(&g, |g| { - let g = g.persistent_graph(); + #[test] + fn test_materialize_deleted_edge() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(1, 0, 1, None).unwrap(); + g.delete_edge(5, 0, 1, None).unwrap(); - let wg = g.window(3, 5); - let mg = wg.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&wg, &mg); - }); -} + let gw = g.window(2, 10); -#[test] -fn test_exploded_latest_time() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.delete_edge(10, 1, 2, None).unwrap(); - assert_eq!(e.latest_time().unwrap().t(), 10); - assert_eq!( - e.explode() - .latest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - vec![Some(10)] - ); -} + let gm = gw.materialize().unwrap(); -#[test] -fn test_exploded_window() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - for t in [5, 10, 15] { - e.add_updates(t, NO_PROPS, None).unwrap(); + assert_graph_equal(&gw, &gm); } - assert_eq!( - e.after(2).explode().time().flatten().collect_vec(), - [3, 5, 10, 15] - ); -} -#[test] -fn test_edge_properties() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, [("test", "test")], None).unwrap(); - assert_eq!(e.properties().get("test").unwrap_str(), "test"); - e.delete(10, None).unwrap(); - assert_eq!(e.properties().get("test").unwrap_str(), "test"); - assert_eq!(e.at(10).properties().get("test"), None); - e.add_updates(11, [("test", "test11")], None).unwrap(); - assert_eq!( - e.window(10, 12).properties().get("test").unwrap_str(), - "test11" - ); - assert_eq!( - e.window(5, 12) - .properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - vec![(5, Prop::str("test")), (11i64, Prop::str("test11"))], - ); -} - -#[test] -fn test_multiple_edge_properties() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test1", "test1")], None).unwrap(); - g.add_edge(1, 0, 1, [("test2", "test2")], None).unwrap(); + #[test] + fn test_addition_deletion_multilayer_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); + g.delete_edge(0, 0, 0, None).unwrap(); + let gw = g.window(0, 0).valid_layers("a"); + let expected_gw = PersistentGraph::new(); + expected_gw.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gw, &expected_gw); + let gwm = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gwm); + } - let e = g.edge(0, 1).unwrap(); - assert_eq!(e.properties().get("test1").unwrap_str(), "test1"); - assert_eq!(e.properties().get("test2").unwrap_str(), "test2"); + #[test] + fn materialize_broken_time() { + let g = PersistentGraph::new(); + g.add_edge( + -7868307470600541330, + 0, + 0, + [("test", Prop::map([("x", "y")]))], + Some("a"), + ) + .unwrap(); + g.add_edge( + -8675512464616562592, + 0, + 0, + [("test", Prop::map([("z", "hi")]))], + None, + ) + .unwrap(); + g.edge(0, 0) + .unwrap() + .add_metadata([("other", "b")], None) + .unwrap(); + let gw = g.window(-7549523977641994620, -995047120251067629); + assert_persistent_materialize_graph_equal(&gw, &gw.materialize().unwrap()) + } - let ew = e.window(1, 10); - assert_eq!(ew.properties().get("test1").unwrap_str(), "test1"); - assert_eq!(ew.properties().get("test2").unwrap_str(), "test2"); -} + #[test] + fn test_materialize_window_start_before_node_add() { + let g = PersistentGraph::new(); + g.add_node(-1, 0, [("test", "test")], None, None).unwrap(); + g.add_node(5, 0, [("test", "blob")], None, None).unwrap(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + let gw = g.window(-5, 8); + let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); + } -#[test] -fn test_edge_history() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.delete(5, None).unwrap(); - e.add_updates(10, NO_PROPS, None).unwrap(); - assert_eq!(e.history(), [0, 10]); - assert_eq!(e.after(1).history(), [10]); - assert!(e.window(1, 4).history().is_empty()); - - // exploded edge still exists - assert_eq!( - e.window(1, 4) - .explode() - .earliest_time() - .flatten() - .collect_vec(), - [1] - ); -} + #[test] + fn test_materialize_edge_metadata() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.add_metadata([("test", "test")], None).unwrap(); + g.delete_edge(1, 1, 2, None).unwrap(); -#[test] -fn test_ordering_of_addition_and_deletion() { - let g = PersistentGraph::new(); + let gw = g.after(1); + let gmw = gw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gw, &gmw); + } - //deletion before addition (deletion has no effect and edge exists (1, inf) - g.delete_edge(1, 1, 2, None).unwrap(); - let e_1_2 = g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); + #[test] + fn test_metadata_multiple_layers() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.add_metadata([("test", "test")], None).unwrap(); + g.delete_edge(1, 1, 2, None).unwrap(); + assert_eq!( + g.edge(1, 2).unwrap().metadata().as_vec(), + [("test".into(), Prop::map([("_default", "test")]))] + ); + let gw = g.after(1); + assert!(gw + .edge(1, 2) + .unwrap() + .metadata() + .iter_filtered() + .next() + .is_none()); + let g_before = g.before(1); + assert_eq!( + g_before.edge(1, 2).unwrap().metadata().as_vec(), + [("test".into(), Prop::map([("_default", "test")]))] + ); + } - //deletion after addition (edge exists only at 2) - let e_3_4 = g.add_edge(2, 3, 4, [("test", "test")], None).unwrap(); - g.delete_edge(2, 3, 4, None).unwrap(); + #[test] + fn test_materialize_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.delete_edge(10, 1, 2, None).unwrap(); - assert_eq!(e_1_2.at(0).properties().get("test"), None); - assert_eq!(e_1_2.at(1).properties().get("test").unwrap_str(), "test"); - assert_eq!(e_1_2.at(2).properties().get("test").unwrap_str(), "test"); + let gm = g + .window(3, 5) + .materialize() + .unwrap() + .into_persistent() + .unwrap(); + assert_persistent_materialize_graph_equal(&g.window(3, 5), &gm); // ignore start of window as it has different updates by design + } - assert_eq!(e_3_4.at(0).properties().get("test"), None); - assert_eq!(e_3_4.at(2).properties().get("test"), None); - assert_eq!(e_3_4.at(3).properties().get("test"), None); + #[test] + fn test_materialize_window_node_props() { + let g = Graph::new(); + g.add_node(0, 1, [("test", "test")], None, None).unwrap(); - assert!(!g.window(0, 1).has_edge(1, 2)); - assert!(!g.window(0, 2).has_edge(3, 4)); - assert!(g.window(1, 2).has_edge(1, 2)); - assert!(!g.window(2, 3).has_edge(3, 4)); // deleted at start of window - assert!(!g.window(3, 4).has_edge(3, 4)); -} + test_storage!(&g, |g| { + let g = g.persistent_graph(); -#[test] -fn test_deletions() { - let edges = [ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - let g = PersistentGraph::new(); - for (t, s, d) in edges.iter() { - g.add_edge(*t, *s, *d, NO_PROPS, None).unwrap(); + let wg = g.window(3, 5); + let mg = wg.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&wg, &mg); + }); } - g.delete_edge(10, edges[0].1, edges[0].2, None).unwrap(); - for (t, s, d) in &edges { - assert!(g.at(*t).has_edge(*s, *d)); - } - assert!(!g.after(10).has_edge(edges[0].1, edges[0].2)); - for (_, s, d) in &edges[1..] { - assert!(g.after(10).has_edge(*s, *d)); + #[test] + fn test_exploded_latest_time() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.delete_edge(10, 1, 2, None).unwrap(); + assert_eq!(e.latest_time().unwrap().t(), 10); + assert_eq!( + e.explode() + .latest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + vec![Some(10)] + ); } - assert_eq!( - g.edge(edges[0].1, edges[0].2) - .unwrap() - .explode() - .latest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(10)] - ); -} - -fn check_valid<'graph, G: GraphViewOps<'graph>>(e: &EdgeView) { - assert!(e.is_valid()); - assert!(!e.is_deleted()); - assert!(e.graph.has_edge(e.src(), e.dst())); - assert!(e.graph.edge(e.src(), e.dst()).is_some()); -} - -fn check_deleted<'graph, G: GraphViewOps<'graph>>(e: &EdgeView) { - assert!(!e.is_valid()); - assert!(e.is_deleted()); - let t = e.latest_time().map(|t| t.t()).unwrap_or(i64::MAX); - let g = e.graph.at(t); // latest view of the graph - assert!(!g.has_edge(e.src(), e.dst())); - assert!(g.edge(e.src(), e.dst()).is_none()); -} - -#[test] -fn test_deletion_multiple_layers() { - let g = PersistentGraph::new(); - - g.add_edge(1, 1, 2, NO_PROPS, Some("1")).unwrap(); - g.delete_edge(2, 1, 2, Some("2")).unwrap(); - g.delete_edge(10, 1, 2, Some("1")).unwrap(); - g.add_edge(10, 1, 2, NO_PROPS, Some("2")).unwrap(); - - let e = g.edge(1, 2).unwrap(); - let e_layer_1 = e.layers("1").unwrap(); - let e_layer_2 = e.layers("2").unwrap(); - assert!(!g.at(0).has_edge(1, 2)); - check_deleted(&e.at(0)); - for t in 1..13 { - assert!(g.at(t).has_edge(1, 2)); - check_valid(&e.at(t)); + #[test] + fn test_exploded_window() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + for t in [5, 10, 15] { + e.add_updates(t, NO_PROPS, None).unwrap(); + } + assert_eq!( + e.after(2).explode().time().flatten().collect_vec(), + [3, 5, 10, 15] + ); } - check_valid(&e); + #[test] + fn test_edge_properties() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, [("test", "test")], None).unwrap(); + assert_eq!(e.properties().get("test").unwrap_str(), "test"); + e.delete(10, None).unwrap(); + assert_eq!(e.properties().get("test").unwrap_str(), "test"); + assert_eq!(e.at(10).properties().get("test"), None); + e.add_updates(11, [("test", "test11")], None).unwrap(); + assert_eq!( + e.window(10, 12).properties().get("test").unwrap_str(), + "test11" + ); + assert_eq!( + e.window(5, 12) + .properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + vec![(5, Prop::str("test")), (11i64, Prop::str("test11"))], + ); + } - check_deleted(&e_layer_1); - check_deleted(&e_layer_1.at(10)); - check_valid(&e_layer_1.at(9)); - check_valid(&e_layer_1.at(1)); - check_deleted(&e_layer_1.at(0)); + #[test] + fn test_multiple_edge_properties() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test1", "test1")], None).unwrap(); + g.add_edge(1, 0, 1, [("test2", "test2")], None).unwrap(); - check_valid(&e_layer_2); - check_deleted(&e_layer_2.at(9)); - check_valid(&e_layer_2.at(10)); -} + let e = g.edge(0, 1).unwrap(); + assert_eq!(e.properties().get("test1").unwrap_str(), "test1"); + assert_eq!(e.properties().get("test2").unwrap_str(), "test2"); -#[test] -fn test_materialize_node_type() { - let g = PersistentGraph::new(); - g.delete_edge(0, 0, 0, None).unwrap(); - g.node(0).unwrap().set_node_type("test").unwrap(); - assert_graph_equal(&g, &g.materialize().unwrap()); -} + let ew = e.window(1, 10); + assert_eq!(ew.properties().get("test1").unwrap_str(), "test1"); + assert_eq!(ew.properties().get("test2").unwrap_str(), "test2"); + } -#[test] -fn test_edge_is_valid() { - let g = PersistentGraph::new(); - - g.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - let e = g.edge(1, 2).unwrap(); - check_deleted(&e.before(1)); - check_valid(&e.after(1)); - check_valid(&e); - - g.add_edge(2, 1, 2, NO_PROPS, Some("1")).unwrap(); - check_valid(&e); - - g.delete_edge(3, 1, 2, Some("1")).unwrap(); - check_valid(&e); - check_deleted(&e.layers("1").unwrap()); - check_deleted(&e.layers("1").unwrap().at(3)); - check_deleted(&e.layers("1").unwrap().after(3)); - check_valid(&e.layers("1").unwrap().before(3)); - check_valid(&e.default_layer()); - - g.delete_edge(4, 1, 2, None).unwrap(); - check_deleted(&e); - check_deleted(&e.layers("1").unwrap()); - check_deleted(&e.default_layer()); - - g.add_edge(5, 1, 2, NO_PROPS, None).unwrap(); - check_valid(&e); - check_valid(&e.default_layer()); - check_deleted(&e.layers("1").unwrap()); -} + #[test] + fn test_edge_history() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.delete(5, None).unwrap(); + e.add_updates(10, NO_PROPS, None).unwrap(); + assert_eq!(e.history(), [0, 10]); + assert_eq!(e.after(1).history(), [10]); + assert!(e.window(1, 4).history().is_empty()); + + // exploded edge still exists + assert_eq!( + e.window(1, 4) + .explode() + .earliest_time() + .flatten() + .collect_vec(), + [1] + ); + } -/// Each layer is handled individually, deletions in one layer do not have an effect on other layers. -/// Layers that have only deletions are ignored -#[test] -fn test_explode_multiple_layers() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); - g.add_edge(1, 1, 2, NO_PROPS, Some("2")).unwrap(); - g.delete_edge(1, 1, 2, Some("1")).unwrap(); - g.delete_edge(2, 1, 2, Some("2")).unwrap(); - g.delete_edge(3, 1, 2, Some("3")).unwrap(); - - let e = g.edge(1, 2).unwrap(); - assert_eq!(e.explode().iter().count(), 2); - assert_eq!(e.before(4).explode().iter().count(), 2); - assert_eq!(e.window(1, 3).explode().iter().count(), 1); - assert_eq!(e.window(2, 3).explode().iter().count(), 0); -} + #[test] + fn test_ordering_of_addition_and_deletion() { + let g = PersistentGraph::new(); -#[test] -fn test_edge_latest_time() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.delete(2, None).unwrap(); - assert_eq!(e.at(2).earliest_time(), None); - assert_eq!(e.at(2).latest_time(), None); - assert!(e.at(2).is_deleted()); - assert_eq!(e.latest_time().unwrap().t(), 2); - e.add_updates(4, NO_PROPS, None).unwrap(); - assert_eq!(e.latest_time().unwrap().t(), 4); - - assert_eq!(e.window(0, 3).latest_time().unwrap().t(), 2); -} + //deletion before addition (deletion has no effect and edge exists (1, inf) + g.delete_edge(1, 1, 2, None).unwrap(); + let e_1_2 = g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); -#[test] -fn test_node_property_semantics() { - let g = PersistentGraph::new(); - let _v = g - .add_node(1, 1, [("test_prop", "test value")], None, None) - .unwrap(); - let v = g - .add_node(11, 1, [("test_prop", "test value 2")], None, None) - .unwrap(); - let v_from_graph = g.at(10).node(1).unwrap(); - assert_eq!(v.properties().get("test_prop").unwrap_str(), "test value 2"); - assert_eq!( - v.at(10).properties().get("test_prop").unwrap_str(), - "test value" - ); - assert_eq!( - v.at(11).properties().get("test_prop").unwrap_str(), - "test value 2" - ); - assert_eq!( - v_from_graph.properties().get("test_prop").unwrap_str(), - "test value" - ); - - assert_eq!( - v.before(11).properties().get("test_prop").unwrap_str(), - "test value" - ); - - assert_eq!( - v.properties() - .temporal() - .get("test_prop") - .unwrap() - .history(), - [1, 11] - ); - assert_eq!( - v_from_graph - .properties() - .temporal() - .get("test_prop") - .unwrap() - .history(), - [10] - ); - - assert_eq!(v_from_graph.earliest_time().map(|t| t.t()), Some(10)); - assert_eq!(v.earliest_time().map(|t| t.t()), Some(1)); - assert_eq!(v.at(10).earliest_time().map(|t| t.t()), Some(10)); - assert_eq!(v.at(10).latest_time().map(|t| t.t()), Some(10)); - assert_eq!(v.latest_time().map(|t| t.t()), Some(11)); -} + //deletion after addition (edge exists only at 2) + let e_3_4 = g.add_edge(2, 3, 4, [("test", "test")], None).unwrap(); + g.delete_edge(2, 3, 4, None).unwrap(); -#[test] -fn test_event_graph() { - let pg = PersistentGraph::new(); - pg.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) - .unwrap(); - pg.delete_edge(10, 0, 1, None).unwrap(); - assert_eq!( - pg.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - - let g = pg.event_graph(); - assert_eq!( - g.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); -} + assert_eq!(e_1_2.at(0).properties().get("test"), None); + assert_eq!(e_1_2.at(1).properties().get("test").unwrap_str(), "test"); + assert_eq!(e_1_2.at(2).properties().get("test").unwrap_str(), "test"); -#[test] -fn test_exploded_latest_time_deleted() { - let g = PersistentGraph::new(); - g.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - g.delete_edge(1, 1, 2, None).unwrap(); + assert_eq!(e_3_4.at(0).properties().get("test"), None); + assert_eq!(e_3_4.at(2).properties().get("test"), None); + assert_eq!(e_3_4.at(3).properties().get("test"), None); - assert_eq!( - g.edge(1, 2) - .unwrap() - .explode() - .latest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(1)] - ); - assert_eq!( - g.edge(1, 2) - .unwrap() - .explode() - .earliest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(1)] - ) -} + assert!(!g.window(0, 1).has_edge(1, 2)); + assert!(!g.window(0, 2).has_edge(3, 4)); + assert!(g.window(1, 2).has_edge(1, 2)); + assert!(!g.window(2, 3).has_edge(3, 4)); // deleted at start of window + assert!(!g.window(3, 4).has_edge(3, 4)); + } -#[test] -fn test_empty_window_has_no_nodes() { - let g = PersistentGraph::new(); - g.add_node(1, 1, NO_PROPS, None, None).unwrap(); - assert_eq!(g.window(2, 2).count_nodes(), 0); - assert_eq!(g.window(1, 1).count_nodes(), 0); - assert_eq!(g.window(0, 0).count_nodes(), 0); -} + #[test] + fn test_deletions() { + let edges = [ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + let g = PersistentGraph::new(); + for (t, s, d) in edges.iter() { + g.add_edge(*t, *s, *d, NO_PROPS, None).unwrap(); + } + g.delete_edge(10, edges[0].1, edges[0].2, None).unwrap(); + + for (t, s, d) in &edges { + assert!(g.at(*t).has_edge(*s, *d)); + } + assert!(!g.after(10).has_edge(edges[0].1, edges[0].2)); + for (_, s, d) in &edges[1..] { + assert!(g.after(10).has_edge(*s, *d)); + } + assert_eq!( + g.edge(edges[0].1, edges[0].2) + .unwrap() + .explode() + .latest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(10)] + ); + } -// #[test] -// fn test_earliest_latest_only_deletion() { -// let g = PersistentGraph::new(); -// g.delete_edge(1, 1, 2, None).unwrap(); -// let gw = g.window(0, 1); -// assert_eq!(gw.earliest_time(), Some(0)); -// assert_eq!(gw.latest_time(), Some(0)); -// } - -#[test] -fn test_earliest_latest_time_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - - assert_eq!(g.window(-1, 0).earliest_time(), None); - assert_eq!(g.window(-1, 0).latest_time(), None); -} + fn check_valid<'graph, G: GraphViewOps<'graph>>(e: &EdgeView) { + assert!(e.is_valid()); + assert!(!e.is_deleted()); + assert!(e.graph.has_edge(e.src(), e.dst())); + assert!(e.graph.edge(e.src(), e.dst()).is_some()); + } -#[test] -fn test_node_earliest_time_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(4, 1, 3, NO_PROPS, None).unwrap(); - - assert_eq!( - g.window(2, 7).node(3).unwrap().earliest_time().unwrap().t(), - 4 - ); - assert_eq!( - g.window(2, 7).node(1).unwrap().earliest_time().unwrap().t(), - 2 - ); -} + fn check_deleted<'graph, G: GraphViewOps<'graph>>(e: &EdgeView) { + assert!(!e.is_valid()); + assert!(e.is_deleted()); + let t = e.latest_time().map(|t| t.t()).unwrap_or(i64::MAX); + let g = e.graph.at(t); // latest view of the graph + assert!(!g.has_edge(e.src(), e.dst())); + assert!(g.edge(e.src(), e.dst()).is_none()); + } -#[test] -fn test_graph_property_semantics() { - let g = PersistentGraph::new(); - g.add_properties(1, [("weight", 10i64)]).unwrap(); - g.add_properties(3, [("weight", 20i64)]).unwrap(); - let prop = g.properties().temporal().get("weight").unwrap(); - - assert_eq!( - prop, - [(EventTime::new(1, 0), 10i64), (EventTime::new(3, 1), 20i64)] - ); - - let prop = g - .window(5, 7) - .properties() - .temporal() - .get("weight") - .unwrap(); - assert_eq!(prop, [(EventTime::new(5, 0), 20i64)]) -} + #[test] + fn test_deletion_multiple_layers() { + let g = PersistentGraph::new(); + + g.add_edge(1, 1, 2, NO_PROPS, Some("1")).unwrap(); + g.delete_edge(2, 1, 2, Some("2")).unwrap(); + g.delete_edge(10, 1, 2, Some("1")).unwrap(); + g.add_edge(10, 1, 2, NO_PROPS, Some("2")).unwrap(); + + let e = g.edge(1, 2).unwrap(); + let e_layer_1 = e.layers("1").unwrap(); + let e_layer_2 = e.layers("2").unwrap(); + + assert!(!g.at(0).has_edge(1, 2)); + check_deleted(&e.at(0)); + for t in 1..13 { + assert!(g.at(t).has_edge(1, 2)); + check_valid(&e.at(t)); + } + + check_valid(&e); + + check_deleted(&e_layer_1); + check_deleted(&e_layer_1.at(10)); + check_valid(&e_layer_1.at(9)); + check_valid(&e_layer_1.at(1)); + check_deleted(&e_layer_1.at(0)); + + check_valid(&e_layer_2); + check_deleted(&e_layer_2.at(9)); + check_valid(&e_layer_2.at(10)); + } -#[test] -fn test_exploded_edge_window() { - let g = PersistentGraph::new(); - g.add_edge(1, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(3, 0, 1, [("test", 3i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); - - let prop_values = g - .window(2, 5) - .edges() - .explode() - .properties() - .map(|props| props.get("test").unwrap_i64()) - .collect::>(); - assert_eq!(prop_values, [1, 3, 4]); -} + #[test] + fn test_materialize_node_type() { + let g = PersistentGraph::new(); + g.delete_edge(0, 0, 0, None).unwrap(); + g.node(0).unwrap().set_node_type("test").unwrap(); + assert_graph_equal(&g, &g.materialize().unwrap()); + } -/// This is a weird edge case -/// -/// An edge deletion creates the corresponding nodes so they should be alive from that point on. -/// We might consider changing the exact semantics here... -#[test] -fn test_node_earliest_latest_time_edge_deletion_only() { - let g = PersistentGraph::new(); - g.delete_edge(10, 0, 1, None).unwrap(); - assert_eq!(g.node(0).unwrap().earliest_time().unwrap().t(), 10); - assert_eq!(g.node(1).unwrap().earliest_time().unwrap().t(), 10); - assert_eq!(g.node(0).unwrap().latest_time().unwrap().t(), 10); - assert_eq!(g.node(1).unwrap().latest_time().unwrap().t(), 10); -} + #[test] + fn test_edge_is_valid() { + let g = PersistentGraph::new(); + + g.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + let e = g.edge(1, 2).unwrap(); + check_deleted(&e.before(1)); + check_valid(&e.after(1)); + check_valid(&e); + + g.add_edge(2, 1, 2, NO_PROPS, Some("1")).unwrap(); + check_valid(&e); + + g.delete_edge(3, 1, 2, Some("1")).unwrap(); + check_valid(&e); + check_deleted(&e.layers("1").unwrap()); + check_deleted(&e.layers("1").unwrap().at(3)); + check_deleted(&e.layers("1").unwrap().after(3)); + check_valid(&e.layers("1").unwrap().before(3)); + check_valid(&e.default_layer()); + + g.delete_edge(4, 1, 2, None).unwrap(); + check_deleted(&e); + check_deleted(&e.layers("1").unwrap()); + check_deleted(&e.default_layer()); + + g.add_edge(5, 1, 2, NO_PROPS, None).unwrap(); + check_valid(&e); + check_valid(&e.default_layer()); + check_deleted(&e.layers("1").unwrap()); + } -/// For an edge the earliest time is the time of the first update (either addition or deletion) -/// -/// The latest time is the time stamp of the last deletion if the last update is a deletion or -/// i64::MAX otherwise. -#[test] -fn test_edge_earliest_latest_time_edge_deletion_only() { - let g = PersistentGraph::new(); - g.delete_edge(10, 0, 1, None).unwrap(); - assert_eq!(g.edge(0, 1).unwrap().earliest_time().unwrap().t(), 10); - assert_eq!(g.edge(0, 1).unwrap().latest_time().unwrap().t(), 10); -} + /// Each layer is handled individually, deletions in one layer do not have an effect on other layers. + /// Layers that have only deletions are ignored + #[test] + fn test_explode_multiple_layers() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); + g.add_edge(1, 1, 2, NO_PROPS, Some("2")).unwrap(); + g.delete_edge(1, 1, 2, Some("1")).unwrap(); + g.delete_edge(2, 1, 2, Some("2")).unwrap(); + g.delete_edge(3, 1, 2, Some("3")).unwrap(); + + let e = g.edge(1, 2).unwrap(); + assert_eq!(e.explode().iter().count(), 2); + assert_eq!(e.before(4).explode().iter().count(), 2); + assert_eq!(e.window(1, 3).explode().iter().count(), 1); + assert_eq!(e.window(2, 3).explode().iter().count(), 0); + } -/// Repeated deletions are ignored, only the first one is relevant. Subsequent deletions do not -/// create exploded edges -#[test] -fn test_repeated_deletions() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(2, 0, 1, None).unwrap(); - g.delete_edge(4, 0, 1, None).unwrap(); - - let e = g.edge(0, 1).unwrap(); - let ex_earliest_t = e - .explode() - .earliest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(); - assert_eq!(ex_earliest_t, [Some(0)]); -} + #[test] + fn test_edge_latest_time() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.delete(2, None).unwrap(); + assert_eq!(e.at(2).earliest_time(), None); + assert_eq!(e.at(2).latest_time(), None); + assert!(e.at(2).is_deleted()); + assert_eq!(e.latest_time().unwrap().t(), 2); + e.add_updates(4, NO_PROPS, None).unwrap(); + assert_eq!(e.latest_time().unwrap().t(), 4); + + assert_eq!(e.window(0, 3).latest_time().unwrap().t(), 2); + } -/// Only additions create exploded edges -#[test] -fn test_only_deletions() { - let g = PersistentGraph::new(); - g.delete_edge(2, 0, 1, None).unwrap(); - g.delete_edge(4, 0, 1, None).unwrap(); + #[test] + fn test_node_property_semantics() { + let g = PersistentGraph::new(); + let _v = g + .add_node(1, 1, [("test_prop", "test value")], None, None) + .unwrap(); + let v = g + .add_node(11, 1, [("test_prop", "test value 2")], None, None) + .unwrap(); + let v_from_graph = g.at(10).node(1).unwrap(); + assert_eq!(v.properties().get("test_prop").unwrap_str(), "test value 2"); + assert_eq!( + v.at(10).properties().get("test_prop").unwrap_str(), + "test value" + ); + assert_eq!( + v.at(11).properties().get("test_prop").unwrap_str(), + "test value 2" + ); + assert_eq!( + v_from_graph.properties().get("test_prop").unwrap_str(), + "test value" + ); + + assert_eq!( + v.before(11).properties().get("test_prop").unwrap_str(), + "test value" + ); + + assert_eq!( + v.properties() + .temporal() + .get("test_prop") + .unwrap() + .history(), + [1, 11] + ); + assert_eq!( + v_from_graph + .properties() + .temporal() + .get("test_prop") + .unwrap() + .history(), + [10] + ); + + assert_eq!(v_from_graph.earliest_time().map(|t| t.t()), Some(10)); + assert_eq!(v.earliest_time().map(|t| t.t()), Some(1)); + assert_eq!(v.at(10).earliest_time().map(|t| t.t()), Some(10)); + assert_eq!(v.at(10).latest_time().map(|t| t.t()), Some(10)); + assert_eq!(v.latest_time().map(|t| t.t()), Some(11)); + } - let e = g.edge(0, 1).unwrap(); - let ex_earliest_t = e.explode().earliest_time().collect_vec(); - assert_eq!(ex_earliest_t, []); -} + #[test] + fn test_event_graph() { + let pg = PersistentGraph::new(); + pg.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) + .unwrap(); + pg.delete_edge(10, 0, 1, None).unwrap(); + assert_eq!( + pg.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + + let g = pg.event_graph(); + assert_eq!( + g.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + } -/// Deletions only bring an edge into the window if they fall inside the window, not if they fall -/// onto the start of the window. The edge is already considered deleted at the time of the -/// deletion event, not at the next step. -#[test] -fn test_only_deletions_window() { - let g = PersistentGraph::new(); - g.delete_edge(1, 0, 1, None).unwrap(); + #[test] + fn test_exploded_latest_time_deleted() { + let g = PersistentGraph::new(); + g.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + g.delete_edge(1, 1, 2, None).unwrap(); + + assert_eq!( + g.edge(1, 2) + .unwrap() + .explode() + .latest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(1)] + ); + assert_eq!( + g.edge(1, 2) + .unwrap() + .explode() + .earliest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(1)] + ) + } - // the edge exists - assert!(g.has_edge(0, 1)); + #[test] + fn test_empty_window_has_no_nodes() { + let g = PersistentGraph::new(); + g.add_node(1, 1, NO_PROPS, None, None).unwrap(); + assert_eq!(g.window(2, 2).count_nodes(), 0); + assert_eq!(g.window(1, 1).count_nodes(), 0); + assert_eq!(g.window(0, 0).count_nodes(), 0); + } - // the deletion falls inside the window so the edge exists - assert!(g.window(0, 2).has_edge(0, 1)); + // #[test] + // fn test_earliest_latest_only_deletion() { + // let g = PersistentGraph::new(); + // g.delete_edge(1, 1, 2, None).unwrap(); + // let gw = g.window(0, 1); + // assert_eq!(gw.earliest_time(), Some(0)); + // assert_eq!(gw.latest_time(), Some(0)); + // } + + #[test] + fn test_earliest_latest_time_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + + assert_eq!(g.window(-1, 0).earliest_time(), None); + assert_eq!(g.window(-1, 0).latest_time(), None); + } - // the deletion falls on the start of the window so the edge is already deleted and does not exist - assert!(!g.window(1, 2).has_edge(0, 1)); + #[test] + fn test_node_earliest_time_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(4, 1, 3, NO_PROPS, None).unwrap(); + + assert_eq!( + g.window(2, 7).node(3).unwrap().earliest_time().unwrap().t(), + 4 + ); + assert_eq!( + g.window(2, 7).node(1).unwrap().earliest_time().unwrap().t(), + 2 + ); + } - // windows that don't contain the deletion event don't have the edge - assert!(!g.window(0, 1).has_edge(0, 1)); - assert!(!g.window(2, 3).has_edge(0, 1)); -} + #[test] + fn test_graph_property_semantics() { + let g = PersistentGraph::new(); + g.add_properties(1, [("weight", 10i64)]).unwrap(); + g.add_properties(3, [("weight", 20i64)]).unwrap(); + let prop = g.properties().temporal().get("weight").unwrap(); -#[test] -fn test_multiple_updates_at_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(0, 0, 1, [("test", 2i64)], None).unwrap(); + assert_eq!( + prop, + [(EventTime::new(1, 0), 10i64), (EventTime::new(3, 1), 20i64)] + ); - let e = g.edge(0, 1).unwrap(); - assert_eq!(e.properties().get("test").unwrap_i64(), 2); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(0, Prop::I64(1)), (0, Prop::I64(2))] - ); - - assert_eq!(e.at(0).properties().get("test").unwrap_i64(), 2); - assert_eq!( - e.at(0) + let prop = g + .window(5, 7) .properties() .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(0, Prop::I64(1)), (0, Prop::I64(2))] - ); - - assert_eq!( - e.at(0) + .get("weight") + .unwrap(); + assert_eq!(prop, [(EventTime::new(5, 0), 20i64)]) + } + + #[test] + fn test_exploded_edge_window() { + let g = PersistentGraph::new(); + g.add_edge(1, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(3, 0, 1, [("test", 3i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let prop_values = g + .window(2, 5) + .edges() .explode() .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [1, 2] - ); - assert_eq!(e.at(0).history(), [0, 0]); - assert_eq!(e.history(), [0, 0]); -} + .map(|props| props.get("test").unwrap_i64()) + .collect::>(); + assert_eq!(prop_values, [1, 3, 4]); + } -#[test] -fn no_persistence_if_updated_at_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + /// This is a weird edge case + /// + /// An edge deletion creates the corresponding nodes so they should be alive from that point on. + /// We might consider changing the exact semantics here... + #[test] + fn test_node_earliest_latest_time_edge_deletion_only() { + let g = PersistentGraph::new(); + g.delete_edge(10, 0, 1, None).unwrap(); + assert_eq!(g.node(0).unwrap().earliest_time().unwrap().t(), 10); + assert_eq!(g.node(1).unwrap().earliest_time().unwrap().t(), 10); + assert_eq!(g.node(0).unwrap().latest_time().unwrap().t(), 10); + assert_eq!(g.node(1).unwrap().latest_time().unwrap().t(), 10); + } - let e = g.edge(0, 1).unwrap().window(2, 5); + /// For an edge the earliest time is the time of the first update (either addition or deletion) + /// + /// The latest time is the time stamp of the last deletion if the last update is a deletion or + /// i64::MAX otherwise. + #[test] + fn test_edge_earliest_latest_time_edge_deletion_only() { + let g = PersistentGraph::new(); + g.delete_edge(10, 0, 1, None).unwrap(); + assert_eq!(g.edge(0, 1).unwrap().earliest_time().unwrap().t(), 10); + assert_eq!(g.edge(0, 1).unwrap().latest_time().unwrap().t(), 10); + } - assert_eq!(e.properties().get("test").unwrap_i64(), 4); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(2, Prop::I64(2)), (4, Prop::I64(4))] - ); - assert_eq!( - e.explode() - .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [2, 4] - ); - assert_eq!(e.history(), [2, 4]); - assert!(e.deletions().is_empty()); -} + /// Repeated deletions are ignored, only the first one is relevant. Subsequent deletions do not + /// create exploded edges + #[test] + fn test_repeated_deletions() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(2, 0, 1, None).unwrap(); + g.delete_edge(4, 0, 1, None).unwrap(); + + let e = g.edge(0, 1).unwrap(); + let ex_earliest_t = e + .explode() + .earliest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(); + assert_eq!(ex_earliest_t, [Some(0)]); + } -#[test] -fn persistence_if_not_updated_at_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + /// Only additions create exploded edges + #[test] + fn test_only_deletions() { + let g = PersistentGraph::new(); + g.delete_edge(2, 0, 1, None).unwrap(); + g.delete_edge(4, 0, 1, None).unwrap(); - let e = g.edge(0, 1).unwrap().window(1, 5); + let e = g.edge(0, 1).unwrap(); + let ex_earliest_t = e.explode().earliest_time().collect_vec(); + assert_eq!(ex_earliest_t, []); + } - assert_eq!(e.properties().get("test").unwrap_i64(), 4); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(1, Prop::I64(1)), (2, Prop::I64(2)), (4, Prop::I64(4))] - ); - assert_eq!( - e.explode() - .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [1, 2, 4] - ); - assert_eq!(e.history(), [2, 4]); // is this actually what we want? - assert!(e.deletions().is_empty()); - assert_eq!(g.window(1, 5).count_temporal_edges(), 3); - assert_eq!(g.window(2, 5).count_temporal_edges(), 2); - assert_eq!(g.window(3, 5).count_temporal_edges(), 2); -} + /// Deletions only bring an edge into the window if they fall inside the window, not if they fall + /// onto the start of the window. The edge is already considered deleted at the time of the + /// deletion event, not at the next step. + #[test] + fn test_only_deletions_window() { + let g = PersistentGraph::new(); + g.delete_edge(1, 0, 1, None).unwrap(); -#[test] -fn no_persistence_if_deleted() { - let g = PersistentGraph::new(); - g.add_edge(-1, 0, 1, [("test", 1i64)], None).unwrap(); - g.delete_edge(0, 0, 1, None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + // the edge exists + assert!(g.has_edge(0, 1)); - let e = g.edge(0, 1).unwrap().window(1, 5); + // the deletion falls inside the window so the edge exists + assert!(g.window(0, 2).has_edge(0, 1)); - assert_eq!(e.properties().get("test").unwrap_i64(), 4); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(2, Prop::I64(2)), (4, Prop::I64(4))] - ); - assert_eq!( - e.explode() - .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [2, 4] - ); - assert_eq!(e.history(), [2, 4]); - assert!(e.deletions().is_empty()); - assert_eq!(g.window(0, 5).count_temporal_edges(), 2); - assert_eq!(g.window(1, 5).count_temporal_edges(), 2); - assert_eq!(g.window(3, 5).count_temporal_edges(), 2); -} + // the deletion falls on the start of the window so the edge is already deleted and does not exist + assert!(!g.window(1, 2).has_edge(0, 1)); -#[test] -fn test_deletion_at_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.delete_edge(2, 0, 1, None).unwrap(); - g.add_edge(2, 0, 1, [("test", 3i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + // windows that don't contain the deletion event don't have the edge + assert!(!g.window(0, 1).has_edge(0, 1)); + assert!(!g.window(2, 3).has_edge(0, 1)); + } - let e = g.edge(0, 1).unwrap().window(2, 5); + #[test] + fn test_multiple_updates_at_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(0, 0, 1, [("test", 2i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap(); + assert_eq!(e.properties().get("test").unwrap_i64(), 2); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(0, Prop::I64(1)), (0, Prop::I64(2))] + ); + + assert_eq!(e.at(0).properties().get("test").unwrap_i64(), 2); + assert_eq!( + e.at(0) + .properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(0, Prop::I64(1)), (0, Prop::I64(2))] + ); + + assert_eq!( + e.at(0) + .explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [1, 2] + ); + assert_eq!(e.at(0).history(), [0, 0]); + assert_eq!(e.history(), [0, 0]); + } - assert_eq!(e.properties().get("test").unwrap_i64(), 4); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(2, Prop::I64(3)), (4, Prop::I64(4))] - ); - - assert_eq!( - e.explode() - .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [3, 4] - ); - - assert!(e.deletions().is_empty()); - assert_eq!(e.history(), [2, 4]); - assert_eq!(g.window(1, 5).count_temporal_edges(), 4); - assert_eq!(g.window(2, 5).count_temporal_edges(), 2); - assert_eq!(g.window(3, 5).count_temporal_edges(), 2); -} + #[test] + fn no_persistence_if_updated_at_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap().window(2, 5); + + assert_eq!(e.properties().get("test").unwrap_i64(), 4); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(2, Prop::I64(2)), (4, Prop::I64(4))] + ); + assert_eq!( + e.explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [2, 4] + ); + assert_eq!(e.history(), [2, 4]); + assert!(e.deletions().is_empty()); + } + + #[test] + fn persistence_if_not_updated_at_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap().window(1, 5); + + assert_eq!(e.properties().get("test").unwrap_i64(), 4); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(1, Prop::I64(1)), (2, Prop::I64(2)), (4, Prop::I64(4))] + ); + assert_eq!( + e.explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [1, 2, 4] + ); + assert_eq!(e.history(), [2, 4]); // is this actually what we want? + assert!(e.deletions().is_empty()); + assert_eq!(g.window(1, 5).count_temporal_edges(), 3); + assert_eq!(g.window(2, 5).count_temporal_edges(), 2); + assert_eq!(g.window(3, 5).count_temporal_edges(), 2); + } -#[test] -fn multiple_node_updates_at_same_time() { - let g = PersistentGraph::new(); + #[test] + fn no_persistence_if_deleted() { + let g = PersistentGraph::new(); + g.add_edge(-1, 0, 1, [("test", 1i64)], None).unwrap(); + g.delete_edge(0, 0, 1, None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap().window(1, 5); + + assert_eq!(e.properties().get("test").unwrap_i64(), 4); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(2, Prop::I64(2)), (4, Prop::I64(4))] + ); + assert_eq!( + e.explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [2, 4] + ); + assert_eq!(e.history(), [2, 4]); + assert!(e.deletions().is_empty()); + assert_eq!(g.window(0, 5).count_temporal_edges(), 2); + assert_eq!(g.window(1, 5).count_temporal_edges(), 2); + assert_eq!(g.window(3, 5).count_temporal_edges(), 2); + } - g.add_node(1, 1, [("prop1", 1)], None, None).unwrap(); - g.add_node(2, 1, [("prop1", 2)], None, None).unwrap(); - g.add_node(2, 1, [("prop1", 3)], None, None).unwrap(); - g.add_node(8, 1, [("prop1", 4)], None, None).unwrap(); - g.add_node(9, 1, [("prop1", 5)], None, None).unwrap(); + #[test] + fn test_deletion_at_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.delete_edge(2, 0, 1, None).unwrap(); + g.add_edge(2, 0, 1, [("test", 3i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap().window(2, 5); + + assert_eq!(e.properties().get("test").unwrap_i64(), 4); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(2, Prop::I64(3)), (4, Prop::I64(4))] + ); + + assert_eq!( + e.explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [3, 4] + ); + + assert!(e.deletions().is_empty()); + assert_eq!(e.history(), [2, 4]); + assert_eq!(g.window(1, 5).count_temporal_edges(), 4); + assert_eq!(g.window(2, 5).count_temporal_edges(), 2); + assert_eq!(g.window(3, 5).count_temporal_edges(), 2); + } - assert_eq!( - g.window(2, 10) - .node(1) - .unwrap() - .properties() - .temporal() - .get("prop1") - .unwrap() - .values() - .collect_vec(), - [Prop::I32(2), Prop::I32(3), Prop::I32(4), Prop::I32(5)] - ) -} + #[test] + fn multiple_node_updates_at_same_time() { + let g = PersistentGraph::new(); + + g.add_node(1, 1, [("prop1", 1)], None, None).unwrap(); + g.add_node(2, 1, [("prop1", 2)], None, None).unwrap(); + g.add_node(2, 1, [("prop1", 3)], None, None).unwrap(); + g.add_node(8, 1, [("prop1", 4)], None, None).unwrap(); + g.add_node(9, 1, [("prop1", 5)], None, None).unwrap(); + + assert_eq!( + g.window(2, 10) + .node(1) + .unwrap() + .properties() + .temporal() + .get("prop1") + .unwrap() + .values() + .collect_vec(), + [Prop::I32(2), Prop::I32(3), Prop::I32(4), Prop::I32(5)] + ) + } -#[test] -fn filtering_all_layers_keeps_explicitly_added_nodes() { - let g = PersistentGraph::new(); - g.add_node(0, 0, [("prop1", false)], None, None).unwrap(); - let view = g.valid_layers(Layer::None).window(0, 1); - assert_eq!(view.count_nodes(), 1); - assert_eq!(view.count_edges(), 0); - assert_graph_equal(&view, &view.materialize().unwrap()) -} + #[test] + fn filtering_all_layers_keeps_explicitly_added_nodes() { + let g = PersistentGraph::new(); + g.add_node(0, 0, [("prop1", false)], None, None).unwrap(); + let view = g.valid_layers(Layer::None).window(0, 1); + assert_eq!(view.count_nodes(), 1); + assert_eq!(view.count_edges(), 0); + assert_graph_equal(&view, &view.materialize().unwrap()) + } -#[test] -fn filtering_all_layers_removes_other_nodes() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + #[test] + fn filtering_all_layers_removes_other_nodes() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - let view = g.valid_layers(Layer::None).window(0, 1); - assert_eq!(view.count_nodes(), 0); - assert_eq!(view.count_edges(), 0); - assert_graph_equal(&view, &view.materialize().unwrap()) -} + let view = g.valid_layers(Layer::None).window(0, 1); + assert_eq!(view.count_nodes(), 0); + assert_eq!(view.count_edges(), 0); + assert_graph_equal(&view, &view.materialize().unwrap()) + } -#[test] -fn deletions_window_has_exclusive_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(2, 0, 1, None).unwrap(); - let e = g.edge(0, 1).unwrap(); - assert!(e.is_active()); // has updates - assert!(!e.is_valid()); // last update is a deletion - assert!(e.is_deleted()); - - assert!(e.window(0, 1).is_active()); // addition in window - assert!(e.window(0, 1).is_valid()); // not deleted - assert!(!e.window(0, 1).is_deleted()); - - assert!(e.window(1, 3).is_active()); // deletion in window - assert!(!e.window(1, 3).is_valid()); - assert!(e.window(1, 3).is_deleted()); - - assert!(!e.window(1, 2).is_active()); // no updates in window - assert!(e.window(1, 2).is_valid()); // deletion not in window (exclusive end) - assert!(!e.window(1, 2).is_deleted()); - - assert!(!e.window(2, 3).is_active()); // deletion at start of window are not included - assert!(!e.window(2, 3).is_valid()); - assert!(e.window(2, 3).is_deleted()); - assert!(!e.latest().is_active()); // this is the same as above + #[test] + fn deletions_window_has_exclusive_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(2, 0, 1, None).unwrap(); + let e = g.edge(0, 1).unwrap(); + assert!(e.is_active()); // has updates + assert!(!e.is_valid()); // last update is a deletion + assert!(e.is_deleted()); + + assert!(e.window(0, 1).is_active()); // addition in window + assert!(e.window(0, 1).is_valid()); // not deleted + assert!(!e.window(0, 1).is_deleted()); + + assert!(e.window(1, 3).is_active()); // deletion in window + assert!(!e.window(1, 3).is_valid()); + assert!(e.window(1, 3).is_deleted()); + + assert!(!e.window(1, 2).is_active()); // no updates in window + assert!(e.window(1, 2).is_valid()); // deletion not in window (exclusive end) + assert!(!e.window(1, 2).is_deleted()); + + assert!(!e.window(2, 3).is_active()); // deletion at start of window are not included + assert!(!e.window(2, 3).is_valid()); + assert!(e.window(2, 3).is_deleted()); + assert!(!e.latest().is_active()); // this is the same as above + } } diff --git a/raphtory/tests/test_edge.rs b/raphtory/tests/test_edge.rs index 1f22b24d6f..2bb247e03b 100644 --- a/raphtory/tests/test_edge.rs +++ b/raphtory/tests/test_edge.rs @@ -1,190 +1,193 @@ -use itertools::Itertools; -use raphtory::{ - db::{ - api::view::Filter, - graph::views::filter::model::{EdgeViewFilterOps, ViewWrapOps}, - }, - prelude::*, - test_storage, - test_utils::test_graph, -}; -use raphtory_api::core::{ - entities::LayerId, - storage::{arc_str::ArcStr, timeindex::AsTime}, -}; -use std::collections::HashMap; - -#[test] -fn test_properties() { - let graph = Graph::new(); - let props = [(ArcStr::from("test"), "test".into_prop())]; - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 2, props.clone(), None).unwrap(); - test_storage!(&graph, |graph| { - let e1 = graph.edge(1, 2).unwrap(); - let e1_w = graph.window(0, 1).edge(1, 2).unwrap(); - assert_eq!( - HashMap::from_iter(e1.properties().as_vec()), - props.into_iter().collect::>() - ); - assert!(e1_w.properties().as_vec().is_empty()) - }); -} +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use raphtory::{ + db::{ + api::view::Filter, + graph::views::filter::model::{EdgeViewFilterOps, ViewWrapOps}, + }, + prelude::*, + test_storage, + test_utils::test_graph, + }; + use raphtory_api::core::{ + entities::LayerId, + storage::{arc_str::ArcStr, timeindex::AsTime}, + }; + use std::collections::HashMap; + + #[test] + fn test_properties() { + let graph = Graph::new(); + let props = [(ArcStr::from("test"), "test".into_prop())]; + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 2, props.clone(), None).unwrap(); + test_storage!(&graph, |graph| { + let e1 = graph.edge(1, 2).unwrap(); + let e1_w = graph.window(0, 1).edge(1, 2).unwrap(); + assert_eq!( + HashMap::from_iter(e1.properties().as_vec()), + props.into_iter().collect::>() + ); + assert!(e1_w.properties().as_vec().is_empty()) + }); + } -#[test] -fn test_metadata() { - let graph = Graph::new(); - graph - .add_edge(1, 1, 2, NO_PROPS, Some("layer 1")) - .unwrap() - .add_metadata([("test_prop", "test_val")], None) - .unwrap(); - graph - .add_edge(1, 2, 3, NO_PROPS, Some("layer 2")) - .unwrap() - .add_metadata([("test_prop", "test_val"), ("other", "2")], None) - .unwrap(); - - graph - .add_edge(1, 2, 3, NO_PROPS, Some("layer 3")) - .unwrap() - .add_metadata([("test_prop", "test_val"), ("other", "3")], None) - .unwrap(); - - // FIXME: #18 metadata prop for edges - test_graph(&graph, |graph| { - assert_eq!( - graph.edge(1, 2).unwrap().metadata().get("test_prop"), - Some(Prop::map([("layer 1", "test_val")])) - ); - assert_eq!( - graph.edge(2, 3).unwrap().metadata().get("test_prop"), - Some(Prop::map([ - ("layer 2", "test_val"), - ("layer 3", "test_val") - ])) - ); + #[test] + fn test_metadata() { + let graph = Graph::new(); + graph + .add_edge(1, 1, 2, NO_PROPS, Some("layer 1")) + .unwrap() + .add_metadata([("test_prop", "test_val")], None) + .unwrap(); + graph + .add_edge(1, 2, 3, NO_PROPS, Some("layer 2")) + .unwrap() + .add_metadata([("test_prop", "test_val"), ("other", "2")], None) + .unwrap(); + + graph + .add_edge(1, 2, 3, NO_PROPS, Some("layer 3")) + .unwrap() + .add_metadata([("test_prop", "test_val"), ("other", "3")], None) + .unwrap(); + + // FIXME: #18 metadata prop for edges + test_graph(&graph, |graph| { + assert_eq!( + graph.edge(1, 2).unwrap().metadata().get("test_prop"), + Some(Prop::map([("layer 1", "test_val")])) + ); + assert_eq!( + graph.edge(2, 3).unwrap().metadata().get("test_prop"), + Some(Prop::map([ + ("layer 2", "test_val"), + ("layer 3", "test_val") + ])) + ); + + assert_eq!( + graph.edge(2, 3).unwrap().metadata().get("other"), + Some(Prop::map([("layer 2", "2"), ("layer 3", "3")])) + ); + + assert_eq!( + graph + .valid_layers(["layer 3", "layer 2"]) + .edge(2, 3) + .unwrap() + .metadata() + .get("other"), + Some(Prop::map([("layer 2", "2"), ("layer 3", "3")])) + ); + + for e in graph.edges() { + for ee in e.explode() { + assert_eq!(ee.metadata().get("test_prop"), Some("test_val".into())) + } + } + }); + } + #[test] + fn test_property_additions() { + let graph = Graph::new(); + let props = [("test", "test")]; + let e1 = graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e1.add_updates(2, props, None).unwrap(); // same layer works + assert!(e1.add_updates(2, props, Some("test2")).is_err()); // different layer is error + let e = graph.edge(1, 2).unwrap(); + e.add_updates(2, props, Some("test2")).unwrap(); // non-restricted edge view can create new layers + let layered_views = e.explode_layers().into_iter().collect_vec(); + for ev in layered_views { + let layer = ev.layer_name().unwrap(); + assert!(ev.add_updates(1, props, Some("test")).is_err()); // restricted edge view cannot create updates in different layer + ev.add_updates(1, [("test2", layer)], None).unwrap() // this will add an update to the same layer as the view (not the default layer) + } + + let e1_w = e1.window(0, 1); assert_eq!( - graph.edge(2, 3).unwrap().metadata().get("other"), - Some(Prop::map([("layer 2", "2"), ("layer 3", "3")])) + e1.properties().as_map(), + props + .into_iter() + .map(|(k, v)| (ArcStr::from(k), v.into_prop())) + .chain([(ArcStr::from("test2"), "_default".into_prop())]) + .collect::>() ); - assert_eq!( - graph - .valid_layers(["layer 3", "layer 2"]) - .edge(2, 3) - .unwrap() - .metadata() - .get("other"), - Some(Prop::map([("layer 2", "2"), ("layer 3", "3")])) + e.layers("test2").unwrap().properties().as_map(), + props + .into_iter() + .map(|(k, v)| (ArcStr::from(k), v.into_prop())) + .chain([(ArcStr::from("test2"), "test2".into_prop())]) + .collect::>() ); - - for e in graph.edges() { - for ee in e.explode() { - assert_eq!(ee.metadata().get("test_prop"), Some("test_val".into())) - } - } - }); -} - -#[test] -fn test_property_additions() { - let graph = Graph::new(); - let props = [("test", "test")]; - let e1 = graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e1.add_updates(2, props, None).unwrap(); // same layer works - assert!(e1.add_updates(2, props, Some("test2")).is_err()); // different layer is error - let e = graph.edge(1, 2).unwrap(); - e.add_updates(2, props, Some("test2")).unwrap(); // non-restricted edge view can create new layers - let layered_views = e.explode_layers().into_iter().collect_vec(); - for ev in layered_views { - let layer = ev.layer_name().unwrap(); - assert!(ev.add_updates(1, props, Some("test")).is_err()); // restricted edge view cannot create updates in different layer - ev.add_updates(1, [("test2", layer)], None).unwrap() // this will add an update to the same layer as the view (not the default layer) + assert_eq!(e1_w.properties().as_map(), HashMap::default()) } - let e1_w = e1.window(0, 1); - assert_eq!( - e1.properties().as_map(), - props - .into_iter() - .map(|(k, v)| (ArcStr::from(k), v.into_prop())) - .chain([(ArcStr::from("test2"), "_default".into_prop())]) - .collect::>() - ); - assert_eq!( - e.layers("test2").unwrap().properties().as_map(), - props - .into_iter() - .map(|(k, v)| (ArcStr::from(k), v.into_prop())) - .chain([(ArcStr::from("test2"), "test2".into_prop())]) - .collect::>() - ); - assert_eq!(e1_w.properties().as_map(), HashMap::default()) -} - -#[test] -fn test_metadata_additions() { - let g = Graph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); - assert_eq!(e.edge.layer(), Some(LayerId(1))); // 0 is static graph - assert!(e.add_metadata([("test1", "test1")], None).is_ok()); // adds properties to layer `"test"` - assert!(e.add_metadata([("test", "test")], Some("test2")).is_err()); // cannot add properties to a different layer - e.add_metadata([("test", "test")], Some("test")).unwrap(); // layer is consistent - assert_eq!(e.metadata().get("test"), Some("test".into())); - assert_eq!(e.metadata().get("test1"), Some("test1".into())); -} + #[test] + fn test_metadata_additions() { + let g = Graph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); + assert_eq!(e.edge.layer(), Some(LayerId(1))); // 0 is static graph + assert!(e.add_metadata([("test1", "test1")], None).is_ok()); // adds properties to layer `"test"` + assert!(e.add_metadata([("test", "test")], Some("test2")).is_err()); // cannot add properties to a different layer + e.add_metadata([("test", "test")], Some("test")).unwrap(); // layer is consistent + assert_eq!(e.metadata().get("test"), Some("test".into())); + assert_eq!(e.metadata().get("test1"), Some("test1".into())); + } -#[test] -fn test_metadata_updates() { - let g = Graph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); - assert!(e.add_metadata([("test1", "test1")], None).is_ok()); // adds properties to layer `"test"` - assert!(e.update_metadata([("test1", "test2")], None).is_ok()); - assert_eq!(e.metadata().get("test1"), Some("test2".into())); -} + #[test] + fn test_metadata_updates() { + let g = Graph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); + assert!(e.add_metadata([("test1", "test1")], None).is_ok()); // adds properties to layer `"test"` + assert!(e.update_metadata([("test1", "test2")], None).is_ok()); + assert_eq!(e.metadata().get("test1"), Some("test2".into())); + } -#[test] -fn test_layers_earliest_time() { - let g = Graph::new(); - let e = g.add_edge(1, 1, 2, NO_PROPS, Some("test")).unwrap(); - assert_eq!(e.earliest_time().map(|t| t.t()), Some(1)); -} + #[test] + fn test_layers_earliest_time() { + let g = Graph::new(); + let e = g.add_edge(1, 1, 2, NO_PROPS, Some("test")).unwrap(); + assert_eq!(e.earliest_time().map(|t| t.t()), Some(1)); + } -#[test] -fn test_edge_layers() { - let graph = Graph::new(); - - graph - .add_edge(0, 1, 2, NO_PROPS, Some("fire_nation")) - .unwrap(); - graph - .add_edge(0, 2, 3, [("a", "test")], Some("fire_nation")) - .unwrap(); - graph - .add_edge(0, 3, 4, [("b", 4)], Some("air_nomads")) - .unwrap(); - - let filter_expr = EdgeFilter.layer("fire_nation").is_active(); - let ids = graph - .filter(filter_expr) - .unwrap() - .edges() - .iter() - .map(|n| n.src().name() + "->" + &n.dst().name()) - .collect::>(); - - assert_eq!(ids, ["1->2", "2->3"]); - - let filter_expr = EdgeFilter.layer("air_nomads").is_active(); - let ids = graph - .filter(filter_expr) - .unwrap() - .edges() - .iter() - .map(|n| n.src().name() + "->" + &n.dst().name()) - .collect::>(); - - assert_eq!(ids, ["3->4"]); + #[test] + fn test_edge_layers() { + let graph = Graph::new(); + + graph + .add_edge(0, 1, 2, NO_PROPS, Some("fire_nation")) + .unwrap(); + graph + .add_edge(0, 2, 3, [("a", "test")], Some("fire_nation")) + .unwrap(); + graph + .add_edge(0, 3, 4, [("b", 4)], Some("air_nomads")) + .unwrap(); + + let filter_expr = EdgeFilter.layer("fire_nation").is_active(); + let ids = graph + .filter(filter_expr) + .unwrap() + .edges() + .iter() + .map(|n| n.src().name() + "->" + &n.dst().name()) + .collect::>(); + + assert_eq!(ids, ["1->2", "2->3"]); + + let filter_expr = EdgeFilter.layer("air_nomads").is_active(); + let ids = graph + .filter(filter_expr) + .unwrap() + .edges() + .iter() + .map(|n| n.src().name() + "->" + &n.dst().name()) + .collect::>(); + + assert_eq!(ids, ["3->4"]); + } } diff --git a/raphtory/tests/test_edge_view.rs b/raphtory/tests/test_edge_view.rs index 577fb6f8c3..8172a74b7a 100644 --- a/raphtory/tests/test_edge_view.rs +++ b/raphtory/tests/test_edge_view.rs @@ -1,161 +1,164 @@ -use raphtory::{prelude::*, test_storage, test_utils::test_graph}; +#[cfg(all(test, feature = "test-utils"))] +mod test { + use raphtory::{prelude::*, test_storage, test_utils::test_graph}; -#[test] -fn test_exploded_edge_properties() { - let graph = Graph::new(); - let actual_prop_values = vec![0, 1, 2, 3]; - for v in actual_prop_values.iter() { - graph.add_edge(0, 1, 2, [("test", *v)], None).unwrap(); - } - - test_storage!(&graph, |graph| { - let prop_values: Vec<_> = graph - .edge(1, 2) - .unwrap() - .explode() - .properties() - .flat_map(|p| p.get("test").into_i32()) - .collect(); - assert_eq!(prop_values, actual_prop_values) - }); -} + #[test] + fn test_exploded_edge_properties() { + let graph = Graph::new(); + let actual_prop_values = vec![0, 1, 2, 3]; + for v in actual_prop_values.iter() { + graph.add_edge(0, 1, 2, [("test", *v)], None).unwrap(); + } -#[test] -fn test_exploded_edge_properties_window() { - let graph = Graph::new(); - let actual_prop_values_0 = vec![0, 1, 2, 3]; - for v in actual_prop_values_0.iter() { - graph.add_edge(0, 1, 2, [("test", *v)], None).unwrap(); + test_storage!(&graph, |graph| { + let prop_values: Vec<_> = graph + .edge(1, 2) + .unwrap() + .explode() + .properties() + .flat_map(|p| p.get("test").into_i32()) + .collect(); + assert_eq!(prop_values, actual_prop_values) + }); } - let actual_prop_values_1 = vec![4, 5, 6]; - for v in actual_prop_values_1.iter() { - graph.add_edge(1, 1, 2, [("test", *v)], None).unwrap(); - } - test_storage!(&graph, |graph| { - let prop_values: Vec<_> = graph - .at(0) - .edge(1, 2) - .unwrap() - .explode() - .properties() - .flat_map(|p| p.get("test").into_i32()) - .collect(); - assert_eq!(prop_values, actual_prop_values_0); - let prop_values: Vec<_> = graph - .at(1) - .edge(1, 2) - .unwrap() - .explode() - .properties() - .flat_map(|p| p.get("test").into_i32()) - .collect(); - assert_eq!(prop_values, actual_prop_values_1) - }); -} -#[test] -fn test_exploded_edge_multilayer() { - let graph = Graph::new(); - let expected_prop_values = vec![0, 1, 2, 3]; - for v in expected_prop_values.iter() { - graph - .add_edge(0, 1, 2, [("test", *v)], Some((v % 2).to_string().as_str())) - .unwrap(); + #[test] + fn test_exploded_edge_properties_window() { + let graph = Graph::new(); + let actual_prop_values_0 = vec![0, 1, 2, 3]; + for v in actual_prop_values_0.iter() { + graph.add_edge(0, 1, 2, [("test", *v)], None).unwrap(); + } + let actual_prop_values_1 = vec![4, 5, 6]; + for v in actual_prop_values_1.iter() { + graph.add_edge(1, 1, 2, [("test", *v)], None).unwrap(); + } + test_storage!(&graph, |graph| { + let prop_values: Vec<_> = graph + .at(0) + .edge(1, 2) + .unwrap() + .explode() + .properties() + .flat_map(|p| p.get("test").into_i32()) + .collect(); + assert_eq!(prop_values, actual_prop_values_0); + let prop_values: Vec<_> = graph + .at(1) + .edge(1, 2) + .unwrap() + .explode() + .properties() + .flat_map(|p| p.get("test").into_i32()) + .collect(); + assert_eq!(prop_values, actual_prop_values_1) + }); } - // FIXME: Needs support for event id from EventTime in disk storage (Issue #30) - test_graph(&graph, |graph| { - let prop_values: Vec<_> = graph - .edge(1, 2) - .unwrap() - .explode() - .properties() - .flat_map(|p| p.get("test").into_i32()) - .collect(); - let actual_layers: Vec<_> = graph - .edge(1, 2) - .unwrap() - .explode() - .layer_name() - .flatten() - .collect(); - let expected_layers: Vec<_> = expected_prop_values - .iter() - .map(|v| (v % 2).to_string()) - .collect(); - assert_eq!(prop_values, expected_prop_values); - assert_eq!(actual_layers, expected_layers); - assert!(graph.edge(1, 2).unwrap().layer_name().is_err()); - assert!(graph.edges().layer_name().all(|l| l.is_err())); - assert!(graph - .edge(1, 2) - .unwrap() - .explode() - .layer_name() - .all(|l| l.is_ok())); - assert!(graph - .edge(1, 2) - .unwrap() - .explode_layers() - .layer_name() - .all(|l| l.is_ok())); - assert!(graph.edges().explode().layer_name().all(|l| l.is_ok())); - assert!(graph - .edges() - .explode_layers() - .layer_name() - .all(|l| l.is_ok())); + #[test] + fn test_exploded_edge_multilayer() { + let graph = Graph::new(); + let expected_prop_values = vec![0, 1, 2, 3]; + for v in expected_prop_values.iter() { + graph + .add_edge(0, 1, 2, [("test", *v)], Some((v % 2).to_string().as_str())) + .unwrap(); + } + // FIXME: Needs support for event id from EventTime in disk storage (Issue #30) + test_graph(&graph, |graph| { + let prop_values: Vec<_> = graph + .edge(1, 2) + .unwrap() + .explode() + .properties() + .flat_map(|p| p.get("test").into_i32()) + .collect(); + let actual_layers: Vec<_> = graph + .edge(1, 2) + .unwrap() + .explode() + .layer_name() + .flatten() + .collect(); + let expected_layers: Vec<_> = expected_prop_values + .iter() + .map(|v| (v % 2).to_string()) + .collect(); + assert_eq!(prop_values, expected_prop_values); + assert_eq!(actual_layers, expected_layers); - assert!(graph.edge(1, 2).unwrap().time().is_err()); - assert!(graph.edges().time().all(|l| l.is_err())); - assert!(graph - .edge(1, 2) - .unwrap() - .explode() - .time() - .all(|l| l.is_ok())); - assert!(graph - .edge(1, 2) - .unwrap() - .explode_layers() - .time() - .all(|l| l.is_err())); - assert!(graph.edges().explode().time().all(|l| l.is_ok())); - assert!(graph.edges().explode_layers().time().all(|l| l.is_err())); - }); -} + assert!(graph.edge(1, 2).unwrap().layer_name().is_err()); + assert!(graph.edges().layer_name().all(|l| l.is_err())); + assert!(graph + .edge(1, 2) + .unwrap() + .explode() + .layer_name() + .all(|l| l.is_ok())); + assert!(graph + .edge(1, 2) + .unwrap() + .explode_layers() + .layer_name() + .all(|l| l.is_ok())); + assert!(graph.edges().explode().layer_name().all(|l| l.is_ok())); + assert!(graph + .edges() + .explode_layers() + .layer_name() + .all(|l| l.is_ok())); + + assert!(graph.edge(1, 2).unwrap().time().is_err()); + assert!(graph.edges().time().all(|l| l.is_err())); + assert!(graph + .edge(1, 2) + .unwrap() + .explode() + .time() + .all(|l| l.is_ok())); + assert!(graph + .edge(1, 2) + .unwrap() + .explode_layers() + .time() + .all(|l| l.is_err())); + assert!(graph.edges().explode().time().all(|l| l.is_ok())); + assert!(graph.edges().explode_layers().time().all(|l| l.is_err())); + }); + } -#[test] -fn test_sorting_by_event_id() { - let graph = Graph::new(); - graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 2, [("second", true)], None).unwrap(); - graph.add_edge(0, 2, 3, [("second", true)], None).unwrap(); + #[test] + fn test_sorting_by_event_id() { + let graph = Graph::new(); + graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 2, [("second", true)], None).unwrap(); + graph.add_edge(0, 2, 3, [("second", true)], None).unwrap(); - //FIXME: DiskGraph does not preserve event id (see #1780) - test_graph(&graph, |graph| { - let mut exploded_edges: Vec<_> = graph.edges().explode().into_iter().collect(); - exploded_edges.sort_by_key(|a| a.time_and_event_id().unwrap()); + //FIXME: DiskGraph does not preserve event id (see #1780) + test_graph(&graph, |graph| { + let mut exploded_edges: Vec<_> = graph.edges().explode().into_iter().collect(); + exploded_edges.sort_by_key(|a| a.time_and_event_id().unwrap()); - let res: Vec<_> = exploded_edges - .into_iter() - .filter_map(|e| { - Some(( - e.src().id().as_u64()?, - e.dst().id().as_u64()?, - e.properties().get("second").into_bool(), - )) - }) - .collect(); - assert_eq!( - res, - vec![ - (2, 3, None), - (1, 2, None), - (1, 2, Some(true)), - (2, 3, Some(true)) - ] - ) - }); + let res: Vec<_> = exploded_edges + .into_iter() + .filter_map(|e| { + Some(( + e.src().id().as_u64()?, + e.dst().id().as_u64()?, + e.properties().get("second").into_bool(), + )) + }) + .collect(); + assert_eq!( + res, + vec![ + (2, 3, None), + (1, 2, None), + (1, 2, Some(true)), + (2, 3, Some(true)) + ] + ) + }); + } } diff --git a/raphtory/tests/test_exploded_edges.rs b/raphtory/tests/test_exploded_edges.rs index 820c91963b..a79a5e49ef 100644 --- a/raphtory/tests/test_exploded_edges.rs +++ b/raphtory/tests/test_exploded_edges.rs @@ -1,550 +1,553 @@ -use itertools::Itertools; -use raphtory::{prelude::*, test_storage}; - -#[test] -fn test_add_node_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph - .add_node((0, 3), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); -} - -#[test] -fn test_add_node_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph - .add_node((0, 1), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node(0) - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph - .add_node((0, 1), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node(0) - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); -} - -#[test] -fn test_create_node_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph - .create_node((0, 3), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); -} - -#[test] -fn test_create_node_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph - .create_node((0, 1), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node(0) - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph - .create_node((0, 1), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node(0) - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); -} - -#[test] -fn test_add_edge_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph.add_edge((0, 3), 0, 1, [("prop", "1")], None).unwrap(); - graph.add_edge((0, 2), 0, 1, [("prop", "2")], None).unwrap(); - graph.add_edge((0, 1), 0, 1, [("prop", "3")], None).unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); -} - -#[test] -fn test_add_edge_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph.add_edge((0, 1), 0, 1, [("prop", "1")], None).unwrap(); - graph.add_edge((0, 1), 0, 1, [("prop", "2")], None).unwrap(); - graph.add_edge((0, 1), 0, 1, [("prop", "3")], None).unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph.add_edge((0, 1), 0, 1, [("prop", "1")], None).unwrap(); - graph.add_edge((0, 2), 0, 1, [("prop", "2")], None).unwrap(); - graph.add_edge((0, 2), 0, 1, [("prop", "3")], None).unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); -} - -#[test] -fn test_add_properties_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph.add_properties((0, 3), [("prop", "1")]).unwrap(); - graph.add_properties((0, 2), [("prop", "2")]).unwrap(); - graph.add_properties((0, 1), [("prop", "3")]).unwrap(); - - let props = graph - .properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); -} - -#[test] -fn test_add_properties_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph.add_properties((0, 1), [("prop", "1")]).unwrap(); - graph.add_properties((0, 1), [("prop", "2")]).unwrap(); - graph.add_properties((0, 1), [("prop", "3")]).unwrap(); - - let props = graph - .properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph.add_edge((0, 1), 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge((0, 2), 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge((0, 2), 0, 1, NO_PROPS, None).unwrap(); - - graph.add_properties((0, 1), [("prop", "1")]).unwrap(); - graph.add_properties((0, 2), [("prop", "2")]).unwrap(); - graph.add_properties((0, 2), [("prop", "3")]).unwrap(); - - let props = graph - .properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); -} - -#[test] -fn test_node_add_updates_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - - graph - .node(0) - .unwrap() - .add_updates((0, 3), [("prop", "1")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 2), [("prop", "2")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "3")], None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); -} - -#[test] -fn test_node_add_updates_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "1")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "2")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "3")], None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "1")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 2), [("prop", "2")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 2), [("prop", "3")], None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); -} - -#[test] -fn test_edge_add_updates_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 3), [("prop", "1")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 2), [("prop", "2")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "3")], None) - .unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); -} - -#[test] -fn test_edge_add_updates_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "1")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "2")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "3")], None) - .unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "1")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 2), [("prop", "2")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 2), [("prop", "3")], None) - .unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); -} - -#[test] -fn test_exploded_edges() { - let graph: Graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(1, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(2, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(3, 0, 1, NO_PROPS, None).unwrap(); - test_storage!(&graph, |graph| { - assert_eq!(graph.count_temporal_edges(), 4) - }); +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use raphtory::{prelude::*, test_storage}; + + #[test] + fn test_add_node_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph + .add_node((0, 3), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); + } + + #[test] + fn test_add_node_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph + .add_node((0, 1), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node(0) + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph + .add_node((0, 1), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node(0) + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); + } + + #[test] + fn test_create_node_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph + .create_node((0, 3), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); + } + + #[test] + fn test_create_node_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph + .create_node((0, 1), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node(0) + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph + .create_node((0, 1), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node(0) + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); + } + + #[test] + fn test_add_edge_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph.add_edge((0, 3), 0, 1, [("prop", "1")], None).unwrap(); + graph.add_edge((0, 2), 0, 1, [("prop", "2")], None).unwrap(); + graph.add_edge((0, 1), 0, 1, [("prop", "3")], None).unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); + } + + #[test] + fn test_add_edge_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph.add_edge((0, 1), 0, 1, [("prop", "1")], None).unwrap(); + graph.add_edge((0, 1), 0, 1, [("prop", "2")], None).unwrap(); + graph.add_edge((0, 1), 0, 1, [("prop", "3")], None).unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph.add_edge((0, 1), 0, 1, [("prop", "1")], None).unwrap(); + graph.add_edge((0, 2), 0, 1, [("prop", "2")], None).unwrap(); + graph.add_edge((0, 2), 0, 1, [("prop", "3")], None).unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); + } + + #[test] + fn test_add_properties_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph.add_properties((0, 3), [("prop", "1")]).unwrap(); + graph.add_properties((0, 2), [("prop", "2")]).unwrap(); + graph.add_properties((0, 1), [("prop", "3")]).unwrap(); + + let props = graph + .properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); + } + + #[test] + fn test_add_properties_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph.add_properties((0, 1), [("prop", "1")]).unwrap(); + graph.add_properties((0, 1), [("prop", "2")]).unwrap(); + graph.add_properties((0, 1), [("prop", "3")]).unwrap(); + + let props = graph + .properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph.add_edge((0, 1), 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge((0, 2), 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge((0, 2), 0, 1, NO_PROPS, None).unwrap(); + + graph.add_properties((0, 1), [("prop", "1")]).unwrap(); + graph.add_properties((0, 2), [("prop", "2")]).unwrap(); + graph.add_properties((0, 2), [("prop", "3")]).unwrap(); + + let props = graph + .properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); + } + + #[test] + fn test_node_add_updates_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + + graph + .node(0) + .unwrap() + .add_updates((0, 3), [("prop", "1")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 2), [("prop", "2")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "3")], None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); + } + + #[test] + fn test_node_add_updates_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "1")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "2")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "3")], None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "1")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 2), [("prop", "2")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 2), [("prop", "3")], None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); + } + + #[test] + fn test_edge_add_updates_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 3), [("prop", "1")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 2), [("prop", "2")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "3")], None) + .unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); + } + + #[test] + fn test_edge_add_updates_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "1")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "2")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "3")], None) + .unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "1")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 2), [("prop", "2")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 2), [("prop", "3")], None) + .unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); + } + + #[test] + fn test_exploded_edges() { + let graph: Graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(1, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(2, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(3, 0, 1, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + assert_eq!(graph.count_temporal_edges(), 4) + }); + } } diff --git a/raphtory/tests/test_filters.rs b/raphtory/tests/test_filters.rs index 37038c2f0d..eb89af59a4 100644 --- a/raphtory/tests/test_filters.rs +++ b/raphtory/tests/test_filters.rs @@ -1,388 +1,182 @@ -use raphtory::{ - db::{api::view::StaticGraphViewOps, graph::assertions::GraphTransformer}, - prelude::*, -}; - -#[cfg(test)] -mod test_composite_filters { +#[cfg(all(test, feature = "test-utils"))] +mod test { use raphtory::{ - db::graph::views::filter::model::{ - edge_filter::EdgeFilter, filter::Filter, node_filter::NodeFilter, - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, - prelude::IntoProp, + db::{api::view::StaticGraphViewOps, graph::assertions::GraphTransformer}, + prelude::*, }; - use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; - - #[test] - fn test_fuzzy_search() { - let filter = Filter::fuzzy_search("name", "pomet", 2, false); - assert!(filter.matches(Some("pometry"))); - - let filter = Filter::fuzzy_search("name", "shivam_kapoor", 2, false); - assert!(filter.matches(Some("shivam_kapoor2"))); - - let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); - assert!(filter.matches(Some("shivam_kapoor2"))); - - let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); - assert!(filter.matches(Some("shivam_kapoor2"))); - - let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); - assert!(!filter.matches(Some("shivam1_kapoor2"))); - - let filter = Filter::fuzzy_search("name", "khivam sapoor", 2, false); - assert!(!filter.matches(Some("shivam1_kapoor2"))); - } - - #[test] - fn test_fuzzy_search_prefix_match() { - let filter = Filter::fuzzy_search("name", "pome", 2, false); - assert!(!filter.matches(Some("pometry"))); - - let filter = Filter::fuzzy_search("name", "pome", 2, true); - assert!(filter.matches(Some("pometry"))); - } - - #[test] - fn test_fuzzy_search_property() { - let filter = NodeFilter.property("prop").fuzzy_search("pomet", 2, false); - assert!(filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); - } - - #[test] - fn test_fuzzy_search_property_prefix_match() { - let filter = EdgeFilter.property("prop").fuzzy_search("pome", 2, false); - assert!(!filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); - - let filter = EdgeFilter.property("prop").fuzzy_search("pome", 2, true); - assert!(filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); - } - #[test] - fn test_contains_match() { - let filter = EdgeFilter.property("prop").contains("shivam"); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); - assert!(res); - let res = filter.matches(None); - assert!(!res); - - let filter = EdgeFilter.property("prop").contains("am_ka"); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); - assert!(res); - } - - #[test] - fn test_contains_not_match() { - let filter = NodeFilter.property("prop").not_contains("shivam"); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); - assert!(!res); - let res = filter.matches(None); - assert!(!res); - } - - #[test] - fn test_is_in_match() { - let filter = NodeFilter - .property("prop") - .is_in(vec!["shivam".into_prop()]); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam")))); - assert!(res); - let res = filter.matches(None); - assert!(!res); - } - - #[test] - fn test_is_not_in_match() { - let filter = EdgeFilter - .property("prop") - .is_not_in(vec!["shivam".into_prop()]); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam")))); - assert!(!res); - let res = filter.matches(None); - assert!(!res); - } -} - -use raphtory_api::core::entities::properties::prop::IntoProp; -use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, -}; - -struct IdentityGraphTransformer; - -impl GraphTransformer for IdentityGraphTransformer { - type Return = G; - fn apply(&self, graph: G) -> Self::Return { - graph - } -} - -#[cfg(test)] -mod test_property_semantics { #[cfg(test)] - mod test_node_property_filter_semantics { - use crate::IdentityGraphTransformer; + mod test_composite_filters { use raphtory::{ - db::{ - api::view::{filter_ops::Filter, StaticGraphViewOps}, - graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, TestVariants, - }, - views::filter::model::{ - node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, TemporalPropertyFilterFactory, - }, - }, + db::graph::views::filter::model::{ + edge_filter::EdgeFilter, filter::Filter, node_filter::NodeFilter, + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, }, - errors::GraphError, - prelude::*, - }; - use raphtory_api::core::entities::properties::prop::Prop; - use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, + prelude::IntoProp, }; + use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; - fn init_graph(graph: G) -> G { - let nodes = [ - (6, "N1", vec![("p1", Prop::U64(2u64))]), - (7, "N1", vec![("p1", Prop::U64(1u64))]), - (6, "N2", vec![("p1", Prop::U64(1u64))]), - (7, "N2", vec![("p1", Prop::U64(2u64))]), - (8, "N3", vec![("p1", Prop::U64(1u64))]), - (9, "N4", vec![("p1", Prop::U64(1u64))]), - (5, "N5", vec![("p1", Prop::U64(1u64))]), - (6, "N5", vec![("p1", Prop::U64(2u64))]), - (5, "N6", vec![("p1", Prop::U64(1u64))]), - (6, "N6", vec![("p1", Prop::U64(1u64))]), - (3, "N7", vec![("p1", Prop::U64(1u64))]), - (5, "N7", vec![("p1", Prop::U64(1u64))]), - (3, "N8", vec![("p1", Prop::U64(1u64))]), - (4, "N8", vec![("p1", Prop::U64(2u64))]), - (2, "N9", vec![("p1", Prop::U64(2u64))]), - (2, "N10", vec![("q1", Prop::U64(0u64))]), - (2, "N10", vec![("p1", Prop::U64(3u64))]), - (2, "N11", vec![("p1", Prop::U64(3u64))]), - (2, "N11", vec![("q1", Prop::U64(0u64))]), - (2, "N12", vec![("q1", Prop::U64(0u64))]), - (3, "N12", vec![("p1", Prop::U64(3u64))]), - (2, "N13", vec![("q1", Prop::U64(0u64))]), - (3, "N13", vec![("p1", Prop::U64(3u64))]), - (2, "N14", vec![("q1", Prop::U64(0u64))]), - (2, "N15", vec![]), - ]; + #[test] + fn test_fuzzy_search() { + let filter = Filter::fuzzy_search("name", "pomet", 2, false); + assert!(filter.matches(Some("pometry"))); - for (id, label, props) in nodes.iter() { - graph - .add_node(*id, label, props.clone(), None, None) - .unwrap(); - } + let filter = Filter::fuzzy_search("name", "shivam_kapoor", 2, false); + assert!(filter.matches(Some("shivam_kapoor2"))); - let metadata = [ - ("N1", [("p1", Prop::U64(1u64))]), - ("N4", [("p1", Prop::U64(2u64))]), - ("N9", [("p1", Prop::U64(1u64))]), - ("N10", [("p1", Prop::U64(1u64))]), - ("N11", [("p1", Prop::U64(1u64))]), - ("N12", [("p1", Prop::U64(1u64))]), - ("N13", [("p1", Prop::U64(1u64))]), - ("N14", [("p1", Prop::U64(1u64))]), - ("N15", [("p1", Prop::U64(1u64))]), - ]; + let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); + assert!(filter.matches(Some("shivam_kapoor2"))); - for (node, props) in metadata.iter() { - graph - .node(node) - .unwrap() - .add_metadata(props.clone()) - .unwrap(); - } + let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); + assert!(filter.matches(Some("shivam_kapoor2"))); - graph - } + let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); + assert!(!filter.matches(Some("shivam1_kapoor2"))); - fn init_graph_for_event_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let graph: G = init_graph(graph); - let nodes = [ - (1, "N16", vec![("p1", Prop::U64(2u64))]), - (1, "N16", vec![("p1", Prop::U64(1u64))]), - (1, "N17", vec![("p1", Prop::U64(1u64))]), - (1, "N17", vec![("p1", Prop::U64(2u64))]), - ]; + let filter = Filter::fuzzy_search("name", "khivam sapoor", 2, false); + assert!(!filter.matches(Some("shivam1_kapoor2"))); + } - for (id, label, props) in nodes.iter() { - graph - .add_node(*id, label, props.clone(), None, None) - .unwrap(); - } + #[test] + fn test_fuzzy_search_prefix_match() { + let filter = Filter::fuzzy_search("name", "pome", 2, false); + assert!(!filter.matches(Some("pometry"))); - graph + let filter = Filter::fuzzy_search("name", "pome", 2, true); + assert!(filter.matches(Some("pometry"))); } #[test] - fn test_metadata_semantics() { - let filter = NodeFilter.metadata("p1").eq(1u64); - let expected_results = vec!["N1", "N10", "N11", "N12", "N13", "N14", "N15", "N9"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + fn test_fuzzy_search_property() { + let filter = NodeFilter.property("prop").fuzzy_search("pomet", 2, false); + assert!(filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); } #[test] - fn test_temporal_any_semantics() { - let filter = NodeFilter.property("p1").temporal().any().eq(1u64); - let expected_results = vec!["N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + fn test_fuzzy_search_property_prefix_match() { + let filter = EdgeFilter.property("prop").fuzzy_search("pome", 2, false); + assert!(!filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); + + let filter = EdgeFilter.property("prop").fuzzy_search("pome", 2, true); + assert!(filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); } #[test] - fn test_temporal_any_semantics_for_event_ids() { - let filter = NodeFilter.property("p1").temporal().any().eq(1u64); - let expected_results = - vec!["N1", "N16", "N17", "N2", "N3", "N4", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + fn test_contains_match() { + let filter = EdgeFilter.property("prop").contains("shivam"); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); + assert!(res); + let res = filter.matches(None); + assert!(!res); + + let filter = EdgeFilter.property("prop").contains("am_ka"); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); + assert!(res); } #[test] - fn test_temporal_latest_semantics() { - let filter = NodeFilter.property("p1").temporal().last().eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + fn test_contains_not_match() { + let filter = NodeFilter.property("prop").not_contains("shivam"); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); + assert!(!res); + let res = filter.matches(None); + assert!(!res); } #[test] - fn test_temporal_latest_semantics_for_event_ids() { - let filter = NodeFilter.property("p1").temporal().last().eq(1u64); - let expected_results = vec!["N1", "N16", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + fn test_is_in_match() { + let filter = NodeFilter + .property("prop") + .is_in(vec!["shivam".into_prop()]); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam")))); + assert!(res); + let res = filter.matches(None); + assert!(!res); } #[test] - fn test_property_semantics() { - // TODO: Const properties not supported for disk_graph. - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + fn test_is_not_in_match() { + let filter = EdgeFilter + .property("prop") + .is_not_in(vec!["shivam".into_prop()]); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam")))); + assert!(!res); + let res = filter.matches(None); + assert!(!res); } + } - #[test] - fn test_property_semantics_for_event_ids() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N16", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + use raphtory_api::core::entities::properties::prop::IntoProp; + use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, + }; + + struct IdentityGraphTransformer; + + impl GraphTransformer for IdentityGraphTransformer { + type Return = G; + fn apply(&self, graph: G) -> Self::Return { + graph } + } - #[test] - fn test_property_semantics_only_metadata() { - // For this graph there won't be any temporal property index for property name "p1". - fn init_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( + #[cfg(test)] + mod test_property_semantics { + #[cfg(test)] + mod test_node_property_filter_semantics { + use crate::test::IdentityGraphTransformer; + use raphtory::{ + db::{ + api::view::{filter_ops::Filter, StaticGraphViewOps}, + graph::{ + assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestVariants, + }, + views::filter::model::{ + node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, TemporalPropertyFilterFactory, + }, + }, + }, + errors::GraphError, + prelude::*, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, + property_addition_ops::InternalPropertyAdditionOps, + }; + + fn init_graph( graph: G, ) -> G { - let nodes = [(2, "N1", vec![("q1", Prop::U64(0u64))]), (2, "N2", vec![])]; + let nodes = [ + (6, "N1", vec![("p1", Prop::U64(2u64))]), + (7, "N1", vec![("p1", Prop::U64(1u64))]), + (6, "N2", vec![("p1", Prop::U64(1u64))]), + (7, "N2", vec![("p1", Prop::U64(2u64))]), + (8, "N3", vec![("p1", Prop::U64(1u64))]), + (9, "N4", vec![("p1", Prop::U64(1u64))]), + (5, "N5", vec![("p1", Prop::U64(1u64))]), + (6, "N5", vec![("p1", Prop::U64(2u64))]), + (5, "N6", vec![("p1", Prop::U64(1u64))]), + (6, "N6", vec![("p1", Prop::U64(1u64))]), + (3, "N7", vec![("p1", Prop::U64(1u64))]), + (5, "N7", vec![("p1", Prop::U64(1u64))]), + (3, "N8", vec![("p1", Prop::U64(1u64))]), + (4, "N8", vec![("p1", Prop::U64(2u64))]), + (2, "N9", vec![("p1", Prop::U64(2u64))]), + (2, "N10", vec![("q1", Prop::U64(0u64))]), + (2, "N10", vec![("p1", Prop::U64(3u64))]), + (2, "N11", vec![("p1", Prop::U64(3u64))]), + (2, "N11", vec![("q1", Prop::U64(0u64))]), + (2, "N12", vec![("q1", Prop::U64(0u64))]), + (3, "N12", vec![("p1", Prop::U64(3u64))]), + (2, "N13", vec![("q1", Prop::U64(0u64))]), + (3, "N13", vec![("p1", Prop::U64(3u64))]), + (2, "N14", vec![("q1", Prop::U64(0u64))]), + (2, "N15", vec![]), + ]; for (id, label, props) in nodes.iter() { graph @@ -392,7 +186,14 @@ mod test_property_semantics { let metadata = [ ("N1", [("p1", Prop::U64(1u64))]), - ("N2", [("p1", Prop::U64(1u64))]), + ("N4", [("p1", Prop::U64(2u64))]), + ("N9", [("p1", Prop::U64(1u64))]), + ("N10", [("p1", Prop::U64(1u64))]), + ("N11", [("p1", Prop::U64(1u64))]), + ("N12", [("p1", Prop::U64(1u64))]), + ("N13", [("p1", Prop::U64(1u64))]), + ("N14", [("p1", Prop::U64(1u64))]), + ("N15", [("p1", Prop::U64(1u64))]), ]; for (node, props) in metadata.iter() { @@ -406,22 +207,7 @@ mod test_property_semantics { graph } - let filter = NodeFilter.property("p1").ge(1u64); - let graph = init_graph(Graph::new()); - assert!(matches!( - graph.filter(filter.clone()).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "p1" - )); - assert!(matches!( - graph.persistent_graph().filter(filter).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "p1" - )); - } - - #[test] - fn test_property_semantics_only_temporal() { - // For this graph there won't be any metadata index for property name "p1". - fn init_graph< + fn init_graph_for_event_ids< G: StaticGraphViewOps + AdditionOps + InternalAdditionOps @@ -430,13 +216,12 @@ mod test_property_semantics { >( graph: G, ) -> G { + let graph: G = init_graph(graph); let nodes = [ - (1, "N1", vec![("p1", Prop::U64(1u64))]), - (2, "N2", vec![("p1", Prop::U64(1u64))]), - (3, "N2", vec![("p1", Prop::U64(2u64))]), - (2, "N3", vec![("p1", Prop::U64(2u64))]), - (3, "N3", vec![("p1", Prop::U64(1u64))]), - (3, "N4", vec![]), + (1, "N16", vec![("p1", Prop::U64(2u64))]), + (1, "N16", vec![("p1", Prop::U64(1u64))]), + (1, "N17", vec![("p1", Prop::U64(1u64))]), + (1, "N17", vec![("p1", Prop::U64(2u64))]), ]; for (id, label, props) in nodes.iter() { @@ -448,484 +233,274 @@ mod test_property_semantics { graph } - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N1", "N3"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - } + #[test] + fn test_metadata_semantics() { + let filter = NodeFilter.metadata("p1").eq(1u64); + let expected_results = vec!["N1", "N10", "N11", "N12", "N13", "N14", "N15", "N9"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[cfg(test)] - mod test_edge_property_filter_semantics { - use crate::IdentityGraphTransformer; - use raphtory::{ - db::{ - api::view::{filter_ops::Filter, EdgeViewOps, StaticGraphViewOps}, - graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, - TestGraphVariants, TestVariants, WindowGraphTransformer, - }, - views::filter::{ - model::{ - edge_filter::EdgeFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, TemporalPropertyFilterFactory, - }, - CreateFilter, - }, - }, - }, - errors::GraphError, - prelude::*, - }; - use raphtory_api::core::entities::properties::prop::Prop; - use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, - }; + #[test] + fn test_temporal_any_semantics() { + let filter = NodeFilter.property("p1").temporal().any().eq(1u64); + let expected_results = vec!["N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - fn init_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let edges = [ - (6, "N1", "N2", vec![("p1", Prop::U64(2u64))]), - (7, "N1", "N2", vec![("p1", Prop::U64(1u64))]), - (6, "N2", "N3", vec![("p1", Prop::U64(1u64))]), - (7, "N2", "N3", vec![("p1", Prop::U64(2u64))]), - (8, "N3", "N4", vec![("p1", Prop::U64(1u64))]), - (9, "N4", "N5", vec![("p1", Prop::U64(1u64))]), - (5, "N5", "N6", vec![("p1", Prop::U64(1u64))]), - (6, "N5", "N6", vec![("p1", Prop::U64(2u64))]), - (5, "N6", "N7", vec![("p1", Prop::U64(1u64))]), - (6, "N6", "N7", vec![("p1", Prop::U64(1u64))]), - (3, "N7", "N8", vec![("p1", Prop::U64(1u64))]), - (5, "N7", "N8", vec![("p1", Prop::U64(1u64))]), - (3, "N8", "N9", vec![("p1", Prop::U64(1u64))]), - (4, "N8", "N9", vec![("p1", Prop::U64(2u64))]), - (2, "N9", "N10", vec![("p1", Prop::U64(2u64))]), - (2, "N10", "N11", vec![("q1", Prop::U64(0u64))]), - (2, "N10", "N11", vec![("p1", Prop::U64(3u64))]), - (2, "N11", "N12", vec![("p1", Prop::U64(3u64))]), - (2, "N11", "N12", vec![("q1", Prop::U64(0u64))]), - (2, "N12", "N13", vec![("q1", Prop::U64(0u64))]), - (3, "N12", "N13", vec![("p1", Prop::U64(3u64))]), - (2, "N13", "N14", vec![("q1", Prop::U64(0u64))]), - (3, "N13", "N14", vec![("p1", Prop::U64(3u64))]), - (2, "N14", "N15", vec![("q1", Prop::U64(0u64))]), - (2, "N15", "N1", vec![]), - ]; + #[test] + fn test_temporal_any_semantics_for_event_ids() { + let filter = NodeFilter.property("p1").temporal().any().eq(1u64); + let expected_results = + vec!["N1", "N16", "N17", "N2", "N3", "N4", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - for (time, src, dst, props) in edges { - graph.add_edge(time, src, dst, props, None).unwrap(); + #[test] + fn test_temporal_latest_semantics() { + let filter = NodeFilter.property("p1").temporal().last().eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); } - let metadata_edges = [ - ("N1", "N2", vec![("p1", Prop::U64(1u64))]), - ("N4", "N5", vec![("p1", Prop::U64(2u64))]), - ("N9", "N10", vec![("p1", Prop::U64(1u64))]), - ("N10", "N11", vec![("p1", Prop::U64(1u64))]), - ("N11", "N12", vec![("p1", Prop::U64(1u64))]), - ("N12", "N13", vec![("p1", Prop::U64(1u64))]), - ("N13", "N14", vec![("p1", Prop::U64(1u64))]), - ("N14", "N15", vec![("p1", Prop::U64(1u64))]), - ("N15", "N1", vec![("p1", Prop::U64(1u64))]), - ]; + #[test] + fn test_temporal_latest_semantics_for_event_ids() { + let filter = NodeFilter.property("p1").temporal().last().eq(1u64); + let expected_results = vec!["N1", "N16", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - for (src, dst, props) in metadata_edges { - graph - .edge(src, dst) - .unwrap() - .add_metadata(props.clone(), None) - .unwrap(); + #[test] + fn test_property_semantics() { + // TODO: Const properties not supported for disk_graph. + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); } - graph - } + #[test] + fn test_property_semantics_for_event_ids() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N16", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - fn init_graph_for_event_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let graph: G = init_graph(graph); - let edge_data = [ - (1, "N16", "N15", vec![("p1", Prop::U64(2u64))]), - (1, "N16", "N15", vec![("p1", Prop::U64(1u64))]), - (1, "N17", "N16", vec![("p1", Prop::U64(1u64))]), - (1, "N17", "N16", vec![("p1", Prop::U64(2u64))]), - ]; + #[test] + fn test_property_semantics_only_metadata() { + // For this graph there won't be any temporal property index for property name "p1". + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes = [(2, "N1", vec![("q1", Prop::U64(0u64))]), (2, "N2", vec![])]; + + for (id, label, props) in nodes.iter() { + graph + .add_node(*id, label, props.clone(), None, None) + .unwrap(); + } + + let metadata = [ + ("N1", [("p1", Prop::U64(1u64))]), + ("N2", [("p1", Prop::U64(1u64))]), + ]; + + for (node, props) in metadata.iter() { + graph + .node(node) + .unwrap() + .add_metadata(props.clone()) + .unwrap(); + } - for (time, src, dst, props) in edge_data { - graph.add_edge(time, src, dst, props, None).unwrap(); + graph + } + + let filter = NodeFilter.property("p1").ge(1u64); + let graph = init_graph(Graph::new()); + assert!(matches!( + graph.filter(filter.clone()).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "p1" + )); + assert!(matches!( + graph.persistent_graph().filter(filter).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "p1" + )); } - graph + #[test] + fn test_property_semantics_only_temporal() { + // For this graph there won't be any metadata index for property name "p1". + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes = [ + (1, "N1", vec![("p1", Prop::U64(1u64))]), + (2, "N2", vec![("p1", Prop::U64(1u64))]), + (3, "N2", vec![("p1", Prop::U64(2u64))]), + (2, "N3", vec![("p1", Prop::U64(2u64))]), + (3, "N3", vec![("p1", Prop::U64(1u64))]), + (3, "N4", vec![]), + ]; + + for (id, label, props) in nodes.iter() { + graph + .add_node(*id, label, props.clone(), None, None) + .unwrap(); + } + + graph + } + + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N1", "N3"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } } - #[test] - fn test_persistent_graph_first_window() { - fn init_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - graph - .add_edge(0, 1, 2, [("p1", Prop::U64(1u64))], None) - .unwrap(); - graph - .add_edge(2, 1, 2, [("p1", Prop::U64(2u64))], None) - .unwrap(); - graph - .add_edge(5, 1, 2, [("p1", Prop::U64(5u64))], None) - .unwrap(); - graph - .add_edge(10, 1, 2, [("p1", Prop::U64(10u64))], None) - .unwrap(); - graph - } - - let filter = EdgeFilter.property("p1").temporal().first().eq(2u64); - - // No window; means the first update is at time 0 and the value of p1 is expected to be 1u64. - let expected_empty = []; - let expected_found = ["1->2"]; - - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_empty, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_empty, - TestVariants::PersistentOnly, - ); - - // Window(1,10); Expected emtpy because the first update is at time 0 and the value of p1 is expected to be 1u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(1..10), - filter.clone(), - &expected_empty, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(1..10), - filter.clone(), - &expected_empty, - TestVariants::PersistentOnly, - ); - - // Window(2,10); Expected update at time 2 and the value of p1 is expected to be 2u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(2..10), - filter.clone(), - &expected_found, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(2..10), - filter.clone(), - &expected_found, - TestVariants::PersistentOnly, - ); - - // Window(3,10); Expected update at time 2 (even if it is outside the window) and the value of p1 is expected to be 2u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(3..10), - filter.clone(), - &expected_found, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(3..10), - filter.clone(), - &expected_found, - TestVariants::PersistentOnly, - ); - - // Window(4,10); Expected update at time 2 (even if it is outside the window) and the value of p1 is expected to be 2u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(4..10), - filter.clone(), - &expected_found, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(4..10), - filter.clone(), - &expected_found, - TestVariants::PersistentOnly, - ); - - // Window(5,10); Expected update at time 5 (even if it is outside the window) and the value of p1 is expected to be 5u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(5..10), - filter.clone(), - &expected_empty, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(5..10), - filter.clone(), - &expected_empty, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_metadata_semantics() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.metadata("p1").eq(1u64); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", "N15->N1", - "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_metadata_semantics2() { - fn filter_edges(graph: &Graph, filter: impl CreateFilter) -> Vec { - let mut results = graph - .filter(filter) - .unwrap() - .edges() - .iter() - .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) - .collect::>(); - results.sort(); - results - } - - let graph = init_graph(Graph::new()); - - let filter = EdgeFilter.metadata("p1").eq(1u64); - assert_eq!( - filter_edges(&graph, filter.clone()), - vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", - "N15->N1", "N9->N10" - ] - ); - - let edge = graph - .add_edge(1, "shivam", "kapoor", [("p1", 100u64)], Some("fire_nation")) - .unwrap(); - edge.add_metadata([("z", true)], Some("fire_nation")) - .unwrap(); - let prop = graph.edge("shivam", "kapoor").unwrap().metadata().get("z"); - assert_eq!(prop, Some(Prop::map([("fire_nation", true)]))); - - let filter2 = EdgeFilter - .metadata("z") - .eq(Prop::map([("fire_nation", true)])); - assert_eq!(filter_edges(&graph, filter2), vec!["shivam->kapoor"]); - - let filter = EdgeFilter - .metadata("p1") - .eq(Prop::map([("_default", 1u64)])); - assert_eq!( - filter_edges(&graph, filter), - vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", - "N15->N1", "N9->N10" - ] - ); - } - - #[test] - fn test_temporal_any_semantics() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").temporal().any().eq(1u64); - let expected_results = vec![ - "N1->N2", "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N9", - ]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_any_semantics_for_event_ids() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").temporal().any().lt(2u64); - let expected_results = vec![ - "N1->N2", "N16->N15", "N17->N16", "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", - "N7->N8", "N8->N9", - ]; - assert_filter_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_latest_semantics() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").temporal().last().eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_latest_semantics_for_event_ids() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").temporal().last().eq(1u64); - let expected_results = - vec!["N1->N2", "N16->N15", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_property_semantics() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").ge(2u64); - let expected_results = vec![ - "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", - "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_property_semantics_for_event_ids() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = - vec!["N1->N2", "N16->N15", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[cfg(test)] + mod test_edge_property_filter_semantics { + use crate::test::IdentityGraphTransformer; + use raphtory::{ + db::{ + api::view::{filter_ops::Filter, EdgeViewOps, StaticGraphViewOps}, + graph::{ + assertions::{ + assert_filter_edges_results, assert_search_edges_results, + TestGraphVariants, TestVariants, WindowGraphTransformer, + }, + views::filter::{ + model::{ + edge_filter::EdgeFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, TemporalPropertyFilterFactory, + }, + CreateFilter, + }, + }, + }, + errors::GraphError, + prelude::*, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, + property_addition_ops::InternalPropertyAdditionOps, + }; - #[test] - fn test_property_semantics_only_metadata() { - // For this graph there won't be any temporal property index for property name "p1". fn init_graph< G: StaticGraphViewOps + AdditionOps @@ -936,8 +511,31 @@ mod test_property_semantics { graph: G, ) -> G { let edges = [ - (2, "N1", "N2", vec![("q1", Prop::U64(0u64))]), - (2, "N2", "N3", vec![]), + (6, "N1", "N2", vec![("p1", Prop::U64(2u64))]), + (7, "N1", "N2", vec![("p1", Prop::U64(1u64))]), + (6, "N2", "N3", vec![("p1", Prop::U64(1u64))]), + (7, "N2", "N3", vec![("p1", Prop::U64(2u64))]), + (8, "N3", "N4", vec![("p1", Prop::U64(1u64))]), + (9, "N4", "N5", vec![("p1", Prop::U64(1u64))]), + (5, "N5", "N6", vec![("p1", Prop::U64(1u64))]), + (6, "N5", "N6", vec![("p1", Prop::U64(2u64))]), + (5, "N6", "N7", vec![("p1", Prop::U64(1u64))]), + (6, "N6", "N7", vec![("p1", Prop::U64(1u64))]), + (3, "N7", "N8", vec![("p1", Prop::U64(1u64))]), + (5, "N7", "N8", vec![("p1", Prop::U64(1u64))]), + (3, "N8", "N9", vec![("p1", Prop::U64(1u64))]), + (4, "N8", "N9", vec![("p1", Prop::U64(2u64))]), + (2, "N9", "N10", vec![("p1", Prop::U64(2u64))]), + (2, "N10", "N11", vec![("q1", Prop::U64(0u64))]), + (2, "N10", "N11", vec![("p1", Prop::U64(3u64))]), + (2, "N11", "N12", vec![("p1", Prop::U64(3u64))]), + (2, "N11", "N12", vec![("q1", Prop::U64(0u64))]), + (2, "N12", "N13", vec![("q1", Prop::U64(0u64))]), + (3, "N12", "N13", vec![("p1", Prop::U64(3u64))]), + (2, "N13", "N14", vec![("q1", Prop::U64(0u64))]), + (3, "N13", "N14", vec![("p1", Prop::U64(3u64))]), + (2, "N14", "N15", vec![("q1", Prop::U64(0u64))]), + (2, "N15", "N1", vec![]), ]; for (time, src, dst, props) in edges { @@ -946,7 +544,14 @@ mod test_property_semantics { let metadata_edges = [ ("N1", "N2", vec![("p1", Prop::U64(1u64))]), - ("N2", "N3", vec![("p1", Prop::U64(1u64))]), + ("N4", "N5", vec![("p1", Prop::U64(2u64))]), + ("N9", "N10", vec![("p1", Prop::U64(1u64))]), + ("N10", "N11", vec![("p1", Prop::U64(1u64))]), + ("N11", "N12", vec![("p1", Prop::U64(1u64))]), + ("N12", "N13", vec![("p1", Prop::U64(1u64))]), + ("N13", "N14", vec![("p1", Prop::U64(1u64))]), + ("N14", "N15", vec![("p1", Prop::U64(1u64))]), + ("N15", "N1", vec![("p1", Prop::U64(1u64))]), ]; for (src, dst, props) in metadata_edges { @@ -960,23 +565,7 @@ mod test_property_semantics { graph } - let filter = EdgeFilter.property("p1").eq(1u64); - let graph = init_graph(Graph::new()); - assert!(matches!( - graph.filter(filter.clone()).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "p1" - )); - assert!(matches!( - graph.persistent_graph().filter(filter).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "p1" - )); - } - - #[test] - fn test_property_semantics_only_temporal() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - // For this graph there won't be any metadata index for property name "p1". - fn init_graph< + fn init_graph_for_event_ids< G: StaticGraphViewOps + AdditionOps + InternalAdditionOps @@ -985,11360 +574,11782 @@ mod test_property_semantics { >( graph: G, ) -> G { - let edges = [ - (1, "N1", "N2", vec![("p1", Prop::U64(1u64))]), - (2, "N2", "N3", vec![("p1", Prop::U64(1u64))]), - (3, "N2", "N3", vec![("p1", Prop::U64(2u64))]), - (2, "N3", "N4", vec![("p1", Prop::U64(2u64))]), - (3, "N3", "N4", vec![("p1", Prop::U64(1u64))]), - (2, "N4", "N5", vec![]), + let graph: G = init_graph(graph); + let edge_data = [ + (1, "N16", "N15", vec![("p1", Prop::U64(2u64))]), + (1, "N16", "N15", vec![("p1", Prop::U64(1u64))]), + (1, "N17", "N16", vec![("p1", Prop::U64(1u64))]), + (1, "N17", "N16", vec![("p1", Prop::U64(2u64))]), ]; - for (time, src, dst, props) in edges { + for (time, src, dst, props) in edge_data { graph.add_edge(time, src, dst, props, None).unwrap(); } graph } - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4"]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - } -} + #[test] + fn test_persistent_graph_first_window() { + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + graph + .add_edge(0, 1, 2, [("p1", Prop::U64(1u64))], None) + .unwrap(); + graph + .add_edge(2, 1, 2, [("p1", Prop::U64(2u64))], None) + .unwrap(); + graph + .add_edge(5, 1, 2, [("p1", Prop::U64(5u64))], None) + .unwrap(); + graph + .add_edge(10, 1, 2, [("p1", Prop::U64(10u64))], None) + .unwrap(); + graph + } -fn init_nodes_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, ->( - graph: G, -) -> G { - let nodes = [ - ( - 1, - "1", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 5u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "2", - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_boat".into_prop()), - ("p40", 10u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "2", - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 15u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 4, - "2", - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 20u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "1", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 10u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 3, - "3", - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - Some("fire_nation"), - ), - ( - 4, - "1", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 15u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 3, - "4", - vec![ - ("p4", "pometry".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - None, - ), - ( - 4, - "4", - vec![ - ("p5", 12u64.into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_ship".into_prop()), - ], - None, - ), - ]; - - for (time, id, props, node_type) in nodes { - graph.add_node(time, id, props, node_type, None).unwrap(); - } + let filter = EdgeFilter.property("p1").temporal().first().eq(2u64); + + // No window; means the first update is at time 0 and the value of p1 is expected to be 1u64. + let expected_empty = []; + let expected_found = ["1->2"]; + + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_empty, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_empty, + TestVariants::PersistentOnly, + ); + + // Window(1,10); Expected emtpy because the first update is at time 0 and the value of p1 is expected to be 1u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(1..10), + filter.clone(), + &expected_empty, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(1..10), + filter.clone(), + &expected_empty, + TestVariants::PersistentOnly, + ); + + // Window(2,10); Expected update at time 2 and the value of p1 is expected to be 2u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(2..10), + filter.clone(), + &expected_found, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(2..10), + filter.clone(), + &expected_found, + TestVariants::PersistentOnly, + ); + + // Window(3,10); Expected update at time 2 (even if it is outside the window) and the value of p1 is expected to be 2u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(3..10), + filter.clone(), + &expected_found, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(3..10), + filter.clone(), + &expected_found, + TestVariants::PersistentOnly, + ); + + // Window(4,10); Expected update at time 2 (even if it is outside the window) and the value of p1 is expected to be 2u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(4..10), + filter.clone(), + &expected_found, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(4..10), + filter.clone(), + &expected_found, + TestVariants::PersistentOnly, + ); + + // Window(5,10); Expected update at time 5 (even if it is outside the window) and the value of p1 is expected to be 5u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(5..10), + filter.clone(), + &expected_empty, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(5..10), + filter.clone(), + &expected_empty, + TestVariants::PersistentOnly, + ); + } - let metadata = [ - ( - "1", - vec![ - ("m1", "pometry".into_prop()), - ("m2", "raphtory".into_prop()), - ], - ), - ("2", vec![("m1", "raphtory".into_prop())]), - ( - "3", - vec![ - ("m2", "pometry".into_prop()), - ("m3", "raphtory".into_prop()), - ], - ), - ( - "4", - vec![ - ("m3", "pometry".into_prop()), - ("m4", "raphtory".into_prop()), - ], - ), - ]; - - for (node_id, md) in metadata { - graph.node(node_id).unwrap().add_metadata(md).unwrap(); - } + #[test] + fn test_metadata_semantics() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.metadata("p1").eq(1u64); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", + "N15->N1", "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - graph -} + #[test] + fn test_metadata_semantics2() { + fn filter_edges(graph: &Graph, filter: impl CreateFilter) -> Vec { + let mut results = graph + .filter(filter) + .unwrap() + .edges() + .iter() + .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) + .collect::>(); + results.sort(); + results + } -fn init_nodes_graph_with_num_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, ->( - graph: G, -) -> G { - let nodes = [ - ( - 1, - 1, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 5u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - 2, - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_boat".into_prop()), - ("p40", 10u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - 2, - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 15u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 4, - 2, - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 20u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - 1, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 10u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 3, - 3, - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - Some("fire_nation"), - ), - ( - 4, - 1, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 15u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 3, - 4, - vec![ - ("p4", "pometry".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - None, - ), - ( - 4, - 4, - vec![ - ("p5", 12u64.into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_ship".into_prop()), - ], - None, - ), - ]; - - for (time, id, props, node_type) in nodes { - graph.add_node(time, id, props, node_type, None).unwrap(); - } + let graph = init_graph(Graph::new()); - graph -} + let filter = EdgeFilter.metadata("p1").eq(1u64); + assert_eq!( + filter_edges(&graph, filter.clone()), + vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", + "N15->N1", "N9->N10" + ] + ); -fn init_nodes_graph_with_str_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, ->( - graph: G, -) -> G { - let nodes = [ - (1, "London", Some("fire_nation")), - (2, "Two", Some("air_nomads")), - (3, "Two", Some("air_nomads")), - (4, "Two", Some("air_nomads")), - (3, "London", Some("fire_nation")), - (3, "Tokyo", Some("fire_nation")), - (4, "London", Some("fire_nation")), - (3, "France Paris", None), - (4, "France Paris", None), - ]; - - for (time, id, node_type) in nodes { - graph.add_node(time, id, NO_PROPS, node_type, None).unwrap(); - } + let edge = graph + .add_edge(1, "shivam", "kapoor", [("p1", 100u64)], Some("fire_nation")) + .unwrap(); + edge.add_metadata([("z", true)], Some("fire_nation")) + .unwrap(); + let prop = graph.edge("shivam", "kapoor").unwrap().metadata().get("z"); + assert_eq!(prop, Some(Prop::map([("fire_nation", true)]))); + + let filter2 = EdgeFilter + .metadata("z") + .eq(Prop::map([("fire_nation", true)])); + assert_eq!(filter_edges(&graph, filter2), vec!["shivam->kapoor"]); + + let filter = EdgeFilter + .metadata("p1") + .eq(Prop::map([("_default", 1u64)])); + assert_eq!( + filter_edges(&graph, filter), + vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", + "N15->N1", "N9->N10" + ] + ); + } - graph -} + #[test] + fn test_temporal_any_semantics() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").temporal().any().eq(1u64); + let expected_results = vec![ + "N1->N2", "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N9", + ]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } -fn init_edges_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, ->( - graph: G, -) -> G { - let edges = [ - ( - 1, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 4u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "2", - "3", - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "2", - "3", - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "3", - "1", - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - Some("fire_nation"), - ), - ( - 3, - "2", - "1", - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - None, - ), - ( - 4, - "David Gilmour", - "John Mayer", - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - None, - ), - ( - 4, - "John Mayer", - "Jimmy Page", - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - None, - ), - ]; - - for (time, src, dst, props, edge_type) in edges { - graph.add_edge(time, src, dst, props, edge_type).unwrap(); - } + #[test] + fn test_temporal_any_semantics_for_event_ids() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").temporal().any().lt(2u64); + let expected_results = vec![ + "N1->N2", "N16->N15", "N17->N16", "N2->N3", "N3->N4", "N4->N5", "N5->N6", + "N6->N7", "N7->N8", "N8->N9", + ]; + assert_filter_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - graph -} + #[test] + fn test_temporal_latest_semantics() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").temporal().last().eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } -fn init_edges_graph2< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, ->( - graph: G, -) -> G { - let edges = [ - ( - 1, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 6u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 7u64.into_prop()), - ("p10", "Gold_ship".into_prop()), - ("p20", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 4u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ], - Some("air_nomads"), - ), - ( - 2, - "2", - "3", - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "2", - "3", - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "3", - "1", - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - Some("air_nomads"), - ), - ( - 3, - "2", - "1", - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - None, - ), - ]; - - for (time, src, dst, props, edge_type) in edges { - graph.add_edge(time, src, dst, props, edge_type).unwrap(); - } + #[test] + fn test_temporal_latest_semantics_for_event_ids() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").temporal().last().eq(1u64); + let expected_results = + vec!["N1->N2", "N16->N15", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - graph -} + #[test] + fn test_property_semantics() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").ge(2u64); + let expected_results = vec![ + "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", + "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } -fn init_edges_graph_with_num_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, ->( - graph: G, -) -> G { - let edges = [ - ( - 1, - 1, - 2, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - 1, - 2, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 4u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - 2, - 3, - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - 2, - 3, - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - 3, - 1, - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - Some("fire_nation"), - ), - ( - 3, - 2, - 1, - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - None, - ), - ]; - - for (time, src, dst, props, edge_type) in edges { - graph.add_edge(time, src, dst, props, edge_type).unwrap(); - } + #[test] + fn test_property_semantics_for_event_ids() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = + vec!["N1->N2", "N16->N15", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - graph -} + #[test] + fn test_property_semantics_only_metadata() { + // For this graph there won't be any temporal property index for property name "p1". + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + (2, "N1", "N2", vec![("q1", Prop::U64(0u64))]), + (2, "N2", "N3", vec![]), + ]; + + for (time, src, dst, props) in edges { + graph.add_edge(time, src, dst, props, None).unwrap(); + } + + let metadata_edges = [ + ("N1", "N2", vec![("p1", Prop::U64(1u64))]), + ("N2", "N3", vec![("p1", Prop::U64(1u64))]), + ]; + + for (src, dst, props) in metadata_edges { + graph + .edge(src, dst) + .unwrap() + .add_metadata(props.clone(), None) + .unwrap(); + } -fn init_edges_graph_with_str_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, ->( - graph: G, -) -> G { - let edges = [ - (1, "London", "Paris", Some("fire_nation")), - (2, "London", "Paris", Some("fire_nation")), - (2, "Two", "Three", Some("air_nomads")), - (3, "Two", "Three", Some("air_nomads")), - (3, "Three", "One", Some("fire_nation")), - (3, "Two", "One", None), - (4, "David Gilmour", "John Mayer", None), - (4, "John Mayer", "Jimmy Page", None), - ]; - - for (time, src, dst, edge_type) in edges { - graph.add_edge(time, src, dst, NO_PROPS, edge_type).unwrap(); - } + graph + } - graph -} + let filter = EdgeFilter.property("p1").eq(1u64); + let graph = init_graph(Graph::new()); + assert!(matches!( + graph.filter(filter.clone()).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "p1" + )); + assert!(matches!( + graph.persistent_graph().filter(filter).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "p1" + )); + } -fn init_edges_graph_with_str_ids_del< - G: StaticGraphViewOps - + AdditionOps - + DeletionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, ->( - graph: G, -) -> G { - let edges = [ - (1, "London", "Paris", Some("fire_nation")), - (2, "London", "Paris", Some("fire_nation")), - (2, "Two", "Three", Some("air_nomads")), - (3, "Two", "Three", Some("air_nomads")), - (3, "Three", "One", Some("fire_nation")), - (3, "Two", "One", None), - (4, "David Gilmour", "John Mayer", None), - (4, "John Mayer", "Jimmy Page", None), - ]; - - for (time, src, dst, edge_type) in edges { - graph.add_edge(time, src, dst, NO_PROPS, edge_type).unwrap(); - } + #[test] + fn test_property_semantics_only_temporal() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + // For this graph there won't be any metadata index for property name "p1". + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + (1, "N1", "N2", vec![("p1", Prop::U64(1u64))]), + (2, "N2", "N3", vec![("p1", Prop::U64(1u64))]), + (3, "N2", "N3", vec![("p1", Prop::U64(2u64))]), + (2, "N3", "N4", vec![("p1", Prop::U64(2u64))]), + (3, "N3", "N4", vec![("p1", Prop::U64(1u64))]), + (2, "N4", "N5", vec![]), + ]; + + for (time, src, dst, props) in edges { + graph.add_edge(time, src, dst, props, None).unwrap(); + } - graph - .delete_edge(3, "London", "Paris", Some("fire_nation")) - .unwrap(); + graph + } - graph - .add_edge(5, "Bangalore", "Bangalore", NO_PROPS, None) - .unwrap(); + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4"]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + } + } - graph -} + fn init_nodes_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes = [ + ( + 1, + "1", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 5u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "2", + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_boat".into_prop()), + ("p40", 10u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "2", + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 15u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 4, + "2", + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 20u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "1", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 10u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 3, + "3", + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + Some("fire_nation"), + ), + ( + 4, + "1", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 15u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 3, + "4", + vec![ + ("p4", "pometry".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + None, + ), + ( + 4, + "4", + vec![ + ("p5", 12u64.into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_ship".into_prop()), + ], + None, + ), + ]; -#[cfg(test)] -mod test_node_filter { - use crate::{ - init_nodes_graph, init_nodes_graph_with_num_ids, init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - }; - use raphtory::{ - algorithms::alternating_mask::alternating_mask, - db::{ - api::view::{filter_ops::NodeSelect, Filter}, - graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - assert_select_nodes_results, TestVariants, - }, - views::filter::model::{ - node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, - ComposableFilter, CompositeNodeFilter, NodeViewFilterOps, TryAsCompositeFilter, - ViewWrapOps, - }, - }, - }, - prelude::{ - AdditionOps, Graph, GraphViewOps, NodeFilter, NodeStateOps, NodeViewOps, TimeOps, - NO_PROPS, - }, - }; + for (time, id, props, node_type) in nodes { + graph.add_node(time, id, props, node_type, None).unwrap(); + } - #[test] - fn test_node_list_is_preserved() { - let graph = init_nodes_graph(Graph::new()); - let nodes = graph - .nodes() - .after(5) - .select(NodeFilter::node_type().contains("x")) - .unwrap(); - let degrees = nodes.degree(); - let degrees_collected = degrees.compute(); - assert_eq!(degrees, degrees_collected); - } + let metadata = [ + ( + "1", + vec![ + ("m1", "pometry".into_prop()), + ("m2", "raphtory".into_prop()), + ], + ), + ("2", vec![("m1", "raphtory".into_prop())]), + ( + "3", + vec![ + ("m2", "pometry".into_prop()), + ("m3", "raphtory".into_prop()), + ], + ), + ( + "4", + vec![ + ("m3", "pometry".into_prop()), + ("m4", "raphtory".into_prop()), + ], + ), + ]; - #[test] - fn test_filter_nodes_for_node_name_eq() { - let filter = NodeFilter::name().eq("3"); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + for (node_id, md) in metadata { + graph.node(node_id).unwrap().add_metadata(md).unwrap(); + } - #[test] - fn test_filter_nodes_for_node_name_ne() { - let filter = NodeFilter::name().ne("2"); - let expected_results = vec!["1", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + graph } - #[test] - fn test_filter_nodes_for_node_name_in() { - let filter = NodeFilter::name().is_in(vec!["1"]); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name().is_in(vec![""]); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name().is_in(vec!["2", "3"]); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + fn init_nodes_graph_with_num_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes = [ + ( + 1, + 1, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 5u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + 2, + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_boat".into_prop()), + ("p40", 10u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + 2, + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 15u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 4, + 2, + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 20u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + 1, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 10u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 3, + 3, + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + Some("fire_nation"), + ), + ( + 4, + 1, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 15u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 3, + 4, + vec![ + ("p4", "pometry".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + None, + ), + ( + 4, + 4, + vec![ + ("p5", 12u64.into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_ship".into_prop()), + ], + None, + ), + ]; - #[test] - fn test_filter_nodes_for_node_name_not_in() { - let filter = NodeFilter::name().is_not_in(vec!["1"]); - let expected_results = vec!["2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name().is_not_in(vec![""]); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + for (time, id, props, node_type) in nodes { + graph.add_node(time, id, props, node_type, None).unwrap(); + } - #[test] - fn test_filter_nodes_for_node_type_eq() { - let filter = NodeFilter::node_type().eq("fire_nation"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + graph } - #[test] - fn test_filter_nodes_for_node_type_ne() { - let filter = NodeFilter::node_type().ne("fire_nation"); - let expected_results = vec!["2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + fn init_nodes_graph_with_str_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes = [ + (1, "London", Some("fire_nation")), + (2, "Two", Some("air_nomads")), + (3, "Two", Some("air_nomads")), + (4, "Two", Some("air_nomads")), + (3, "London", Some("fire_nation")), + (3, "Tokyo", Some("fire_nation")), + (4, "London", Some("fire_nation")), + (3, "France Paris", None), + (4, "France Paris", None), + ]; - #[test] - fn test_filter_nodes_for_node_type_in() { - let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomads"]); - let expected_results = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + for (time, id, node_type) in nodes { + graph.add_node(time, id, NO_PROPS, node_type, None).unwrap(); + } - #[test] - fn test_filter_nodes_for_node_type_not_in() { - let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); - let expected_results = vec!["2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + graph } - #[test] - fn test_filter_nodes_for_node_type_starts_with() { - let filter = NodeFilter::node_type().starts_with("fire"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().starts_with("rocket"); - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + fn init_edges_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + ( + 1, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 4u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "2", + "3", + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "2", + "3", + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "3", + "1", + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + Some("fire_nation"), + ), + ( + 3, + "2", + "1", + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + None, + ), + ( + 4, + "David Gilmour", + "John Mayer", + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + None, + ), + ( + 4, + "John Mayer", + "Jimmy Page", + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + None, + ), + ]; - #[test] - fn test_filter_nodes_for_node_type_ends_with() { - let filter = NodeFilter::node_type().ends_with("nomads"); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().ends_with("circle"); - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + for (time, src, dst, props, edge_type) in edges { + graph.add_edge(time, src, dst, props, edge_type).unwrap(); + } - #[test] - fn test_filter_nodes_for_node_type_contains() { - let filter = NodeFilter::node_type().contains("fire"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + graph } - #[test] - fn test_filter_nodes_for_node_type_contains_not() { - let filter = NodeFilter::node_type().not_contains("fire"); - let expected_results = vec!["2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + fn init_edges_graph2< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + ( + 1, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 6u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 7u64.into_prop()), + ("p10", "Gold_ship".into_prop()), + ("p20", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 4u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ], + Some("air_nomads"), + ), + ( + 2, + "2", + "3", + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "2", + "3", + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "3", + "1", + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + Some("air_nomads"), + ), + ( + 3, + "2", + "1", + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + None, + ), + ]; - #[test] - fn test_filter_nodes_for_fuzzy_search() { - let filter = NodeFilter::node_type().fuzzy_search("fire", 2, true); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().fuzzy_search("fire", 2, false); - let expected_results: Vec<&str> = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().fuzzy_search("air_noma", 2, false); - let expected_results: Vec<&str> = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + for (time, src, dst, props, edge_type) in edges { + graph.add_edge(time, src, dst, props, edge_type).unwrap(); + } - #[test] - fn test_filter_nodes_for_not_node_type() { - let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]).not(); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + graph } - #[test] - fn test_filter_nodes_for_eq_node_id() { - let filter = NodeFilter::id().eq("1"); - let expected_results = vec!["1"]; - - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); + fn init_edges_graph_with_num_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + ( + 1, + 1, + 2, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + 1, + 2, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 4u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + 2, + 3, + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + 2, + 3, + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + 3, + 1, + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + Some("fire_nation"), + ), + ( + 3, + 2, + 1, + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + None, + ), + ]; - let filter = NodeFilter::id().eq(1); - let expected_results = vec!["1"]; + for (time, src, dst, props, edge_type) in edges { + graph.add_edge(time, src, dst, props, edge_type).unwrap(); + } - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); + graph } - #[test] - fn test_filter_nodes_for_ne_node_id() { - let filter = NodeFilter::id().ne("1"); - let expected_results = vec!["2", "3", "4"]; - - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + fn init_edges_graph_with_str_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + (1, "London", "Paris", Some("fire_nation")), + (2, "London", "Paris", Some("fire_nation")), + (2, "Two", "Three", Some("air_nomads")), + (3, "Two", "Three", Some("air_nomads")), + (3, "Three", "One", Some("fire_nation")), + (3, "Two", "One", None), + (4, "David Gilmour", "John Mayer", None), + (4, "John Mayer", "Jimmy Page", None), + ]; - let filter = NodeFilter::id().ne(1); - let expected_results = vec!["2", "3", "4"]; + for (time, src, dst, edge_type) in edges { + graph.add_edge(time, src, dst, NO_PROPS, edge_type).unwrap(); + } - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + graph } - #[test] - fn test_filter_nodes_for_is_in_node_id() { - let filter = NodeFilter::id().is_in(vec!["1", "3", "6"]); - let expected_results = vec!["1", "3"]; - - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::id().is_in(vec![1, 3, 6]); - let expected_results = vec!["1", "3"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + fn init_edges_graph_with_str_ids_del< + G: StaticGraphViewOps + + AdditionOps + + DeletionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + (1, "London", "Paris", Some("fire_nation")), + (2, "London", "Paris", Some("fire_nation")), + (2, "Two", "Three", Some("air_nomads")), + (3, "Two", "Three", Some("air_nomads")), + (3, "Three", "One", Some("fire_nation")), + (3, "Two", "One", None), + (4, "David Gilmour", "John Mayer", None), + (4, "John Mayer", "Jimmy Page", None), + ]; - #[test] - fn test_filter_nodes_for_is_not_in_node_id() { - let filter = NodeFilter::id().is_not_in(vec!["1", "3", "6"]); - let expected_results = vec!["2", "4"]; + for (time, src, dst, edge_type) in edges { + graph.add_edge(time, src, dst, NO_PROPS, edge_type).unwrap(); + } - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + graph + .delete_edge(3, "London", "Paris", Some("fire_nation")) + .unwrap(); - let filter = NodeFilter::id().is_not_in(vec![1, 3, 6]); - let expected_results = vec!["2", "4"]; + graph + .add_edge(5, "Bangalore", "Bangalore", NO_PROPS, None) + .unwrap(); - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + graph } - #[test] - fn test_filter_nodes_for_lt_node_id() { - let filter = NodeFilter::id().lt(2); - let expected_results = vec!["1"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, + mod test_node_filter { + use crate::test::{ + init_nodes_graph, init_nodes_graph_with_num_ids, init_nodes_graph_with_str_ids, IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_le_node_id() { - let filter = NodeFilter::id().le(3); - let expected_results = vec!["1", "2", "3"]; + }; + use raphtory::{ + algorithms::alternating_mask::alternating_mask, + db::{ + api::view::{filter_ops::NodeSelect, Filter}, + graph::{ + assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, + assert_select_nodes_results, TestVariants, + }, + views::filter::model::{ + node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, + ComposableFilter, CompositeNodeFilter, NodeViewFilterOps, + TryAsCompositeFilter, ViewWrapOps, + }, + }, + }, + prelude::{ + AdditionOps, Graph, GraphViewOps, NodeFilter, NodeStateOps, NodeViewOps, TimeOps, + NO_PROPS, + }, + }; - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_node_list_is_preserved() { + let graph = init_nodes_graph(Graph::new()); + let nodes = graph + .nodes() + .after(5) + .select(NodeFilter::node_type().contains("x")) + .unwrap(); + let degrees = nodes.degree(); + let degrees_collected = degrees.compute(); + assert_eq!(degrees, degrees_collected); + } - #[test] - fn test_filter_nodes_for_gt_node_id() { - let filter = NodeFilter::id().gt(2); - let expected_results = vec!["3", "4"]; + #[test] + fn test_filter_nodes_for_node_name_eq() { + let filter = NodeFilter::name().eq("3"); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_node_name_ne() { + let filter = NodeFilter::name().ne("2"); + let expected_results = vec!["1", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_ge_node_id() { - let filter = NodeFilter::id().ge(2); - let expected_results = vec!["2", "3", "4"]; + #[test] + fn test_filter_nodes_for_node_name_in() { + let filter = NodeFilter::name().is_in(vec!["1"]); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter::name().is_in(vec![""]); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_nodes_for_starts_with_node_id() { - let filter = NodeFilter::id().starts_with("France"); - let expected_results = vec!["France Paris"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter::name().is_in(vec!["2", "3"]); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_ends_with_node_id() { - let filter = NodeFilter::id().ends_with("wo"); - let expected_results = vec!["Two"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_node_name_not_in() { + let filter = NodeFilter::name().is_not_in(vec!["1"]); + let expected_results = vec!["2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_nodes_for_contains_node_id() { - let filter = NodeFilter::id().contains("o"); - let expected_results = vec!["London", "Tokyo", "Two"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter::name().is_not_in(vec![""]); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_not_contains_node_id() { - let filter = NodeFilter::id().not_contains("o"); - let expected_results = vec!["France Paris"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_node_type_eq() { + let filter = NodeFilter::node_type().eq("fire_nation"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_is_in_node_id_str() { - let filter = NodeFilter::id().is_in(vec!["London", "Tokyo"]); - let expected_results = vec!["London", "Tokyo"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_node_type_ne() { + let filter = NodeFilter::node_type().ne("fire_nation"); + let expected_results = vec!["2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_is_not_in_node_id_str() { - let filter = NodeFilter::id().is_not_in(vec!["London", "Tokyo"]); - let expected_results = vec!["France Paris", "Two"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_node_type_in() { + let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_is_active_node_window() { - let filter = NodeFilter.window(1, 10).is_active(); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomads"]); + let expected_results = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_is_active_node_window_not() { - let filter = NodeFilter - .window(1, 10) - .is_active() - .try_as_composite_node_filter() - .unwrap(); - let filter = CompositeNodeFilter::Not(Box::new(filter)); - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_filter_nodes_for_node_type_not_in() { + let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); + let expected_results = vec!["2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_is_active_node_latest() { - let filter = NodeFilter.latest().is_active(); - let expected_results = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_filter_nodes_for_node_type_starts_with() { + let filter = NodeFilter::node_type().starts_with("fire"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_by_column() { - let graph = Graph::new(); - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 3, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 5, NO_PROPS, None, None).unwrap(); - - let mask = alternating_mask(&graph); - let expected_nodes: Vec<_> = graph - .nodes() - .name() - .iter_values() - .skip(1) - .step_by(2) - .collect(); - - let filtered = graph - .filter(NodeFilter::by_column(&mask, "bool_col").unwrap()) - .unwrap(); + let filter = NodeFilter::node_type().starts_with("rocket"); + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - let names = filtered - .nodes() - .iter() - .map(|n| n.id().to_string()) - .collect::>(); + #[test] + fn test_filter_nodes_for_node_type_ends_with() { + let filter = NodeFilter::node_type().ends_with("nomads"); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - assert_eq!(names, expected_nodes); + let filter = NodeFilter::node_type().ends_with("circle"); + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - let filtered = graph - .nodes() - .select(NodeFilter::by_column(&mask, "bool_col").unwrap()) - .unwrap(); + #[test] + fn test_filter_nodes_for_node_type_contains() { + let filter = NodeFilter::node_type().contains("fire"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - let names = filtered - .iter() - .map(|n| n.id().to_string()) - .collect::>(); + #[test] + fn test_filter_nodes_for_node_type_contains_not() { + let filter = NodeFilter::node_type().not_contains("fire"); + let expected_results = vec!["2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - assert_eq!(names, expected_nodes); - } + #[test] + fn test_filter_nodes_for_fuzzy_search() { + let filter = NodeFilter::node_type().fuzzy_search("fire", 2, true); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_is_active_node_snapshot_at() { - let filter = NodeFilter.snapshot_at(2).is_active(); - let expected_results = vec!["2"]; - assert_select_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } -} + let filter = NodeFilter::node_type().fuzzy_search("fire", 2, false); + let expected_results: Vec<&str> = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); -#[cfg(test)] -mod test_node_property_filter { - use crate::{init_nodes_graph, IdentityGraphTransformer}; - use raphtory::db::graph::{ - assertions::{assert_filter_nodes_results, assert_search_nodes_results, TestVariants}, - views::filter::model::{ - graph_filter::GraphFilter, - node_filter::NodeFilter, - not_filter::NotFilter, - property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, - windowed_filter::Windowed, - ComposableFilter, PropertyFilterFactory, TemporalPropertyFilterFactory, ViewWrapOps, - }, - }; - use raphtory_api::core::entities::properties::prop::Prop; - use std::vec; - - #[test] - fn test_exact_match() { - // let filter = NodeFilter.degree > 5 - let filter = NodeFilter.property("p10").eq("Paper_airplane"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p10").eq(""); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter::node_type().fuzzy_search("air_noma", 2, false); + let expected_results: Vec<&str> = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_not_exact_match() { - let filter = NodeFilter.property("p10").eq("Paper"); - let expected_results: Vec<&str> = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_not_node_type() { + let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]).not(); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_property_eq() { - let filter = NodeFilter.property("p2").eq(2u64); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p30").temporal().first().eq("Old_boat"); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p20").temporal().all().eq("Gold_ship"); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_eq_node_id() { + let filter = NodeFilter::id().eq("1"); + let expected_results = vec!["1"]; - #[test] - fn test_filter_nodes_for_property_ne() { - let filter = NodeFilter.property("p2").ne(2u64); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p30").temporal().first().ne("Old_boat"); - let expected_results = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p1").temporal().all().ne("Gold_ship"); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_nodes_for_property_lt() { - let filter = NodeFilter.property("p2").lt(10u64); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").temporal().first().lt(10u64); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p9").temporal().all().lt(10u64); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_le() { - let filter = NodeFilter.property("p2").le(6u64); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p9").temporal().first().le(10u64); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p2").temporal().all().le(10u64); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter::id().eq(1); + let expected_results = vec!["1"]; - #[test] - fn test_filter_nodes_for_property_gt() { - let filter = NodeFilter.property("p2").gt(2u64); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").temporal().first().gt(5u64); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p9").temporal().all().gt(1u64); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_property_ge() { - let filter = NodeFilter.property("p2").ge(2u64); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").temporal().first().ge(5u64); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").temporal().all().ge(5u64); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_ne_node_id() { + let filter = NodeFilter::id().ne("1"); + let expected_results = vec!["2", "3", "4"]; - #[test] - fn test_filter_nodes_for_property_in() { - let filter = NodeFilter.property("p2").is_in(vec![Prop::U64(6)]); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .is_in(vec![Prop::U64(2), Prop::U64(6)]); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p40") - .temporal() - .first() - .is_in(vec![Prop::U64(5)]); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .temporal() - .any() - .is_in(vec![Prop::U64(2)]); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_nodes_for_property_not_in() { - let filter = NodeFilter.property("p2").is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .temporal() - .all() - .is_not_in(vec![Prop::U64(2)]); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter::id().ne(1); + let expected_results = vec!["2", "3", "4"]; - #[test] - fn test_filter_nodes_for_property_is_some() { - let filter = NodeFilter.property("p2").is_some(); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").is_some(); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_property_is_none() { - let filter = NodeFilter.property("p2").is_none(); - let expected_results = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").is_none(); - let expected_results = vec!["3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_is_in_node_id() { + let filter = NodeFilter::id().is_in(vec!["1", "3", "6"]); + let expected_results = vec!["1", "3"]; - #[test] - fn test_filter_nodes_for_property_starts_with() { - let filter = NodeFilter.property("p10").starts_with("Pa"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .any() - .starts_with("Pap"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .starts_with("Pape"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .starts_with("Yohan"); - let expected_results: Vec<&str> = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p30") - .temporal() - .first() - .starts_with("Gold"); - let expected_results: Vec<&str> = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p20") - .temporal() - .all() - .starts_with("Gold"); - let expected_results: Vec<&str> = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_nodes_for_property_ends_with() { - let filter = NodeFilter.property("p10").ends_with("lane"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .any() - .ends_with("ship"); - let expected_results: Vec<&str> = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .ends_with("ane"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .ends_with("Jerry"); - let expected_results: Vec<&str> = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p20") - .temporal() - .first() - .ends_with("boat"); - let expected_results: Vec<&str> = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p20") - .temporal() - .all() - .ends_with("ship"); - let expected_results: Vec<&str> = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter::id().is_in(vec![1, 3, 6]); + let expected_results = vec!["1", "3"]; - #[test] - fn test_filter_nodes_for_property_contains() { - let filter = NodeFilter.property("p10").contains("Paper"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .any() - .contains("Paper"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .contains("Paper"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p30") - .temporal() - .first() - .contains("Old"); - let expected_results: Vec<&str> = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p30").temporal().all().contains("Gold"); - let expected_results: Vec<&str> = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_property_contains_not() { - let filter = NodeFilter.property("p10").not_contains("ship"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .any() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p30") - .temporal() - .first() - .not_contains("Old"); - let expected_results: Vec<&str> = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p30") - .temporal() - .all() - .not_contains("boat"); - let expected_results: Vec<&str> = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_is_not_in_node_id() { + let filter = NodeFilter::id().is_not_in(vec!["1", "3", "6"]); + let expected_results = vec!["2", "4"]; - #[test] - fn test_filter_nodes_for_not_property() { - let filter = NotFilter(NodeFilter.property("p10").contains("Paper")); - let expected_results: Vec<&str> = vec!["4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p10").contains("Paper").not(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_nodes_for_temporal_property_sum() { - let filter = NodeFilter.property("p9").temporal().sum().eq(15u64); - let expected_results: Vec<&str> = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter::id().is_not_in(vec![1, 3, 6]); + let expected_results = vec!["2", "4"]; - #[test] - fn test_filter_nodes_for_temporal_property_avg() { - let filter = NodeFilter.property("p2").temporal().avg().le(10f64); - let expected_results: Vec<&str> = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_temporal_property_min() { - let filter = NodeFilter.property("p40").temporal().min().is_in(vec![ - Prop::U64(5), - Prop::U64(10), - Prop::U64(20), - ]); - let expected_results: Vec<&str> = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_lt_node_id() { + let filter = NodeFilter::id().lt(2); + let expected_results = vec!["1"]; - #[test] - fn test_filter_nodes_for_temporal_property_max() { - let filter = NodeFilter.property("p3").temporal().max().is_not_in(vec![ - Prop::U64(5), - Prop::U64(10), - Prop::U64(20), - ]); - let expected_results: Vec<&str> = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_nodes_for_temporal_property_len() { - let filter = NodeFilter.property("p2").temporal().len().le(5u64); - let expected_results: Vec<&str> = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_le_node_id() { + let filter = NodeFilter::id().le(3); + let expected_results = vec!["1", "2", "3"]; - #[test] - fn test_nodes_window_filter() { - let filter = NodeFilter - .window(1, 3) - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - // Wider window includes node 3 - let filter = NodeFilter - .window(1, 5) - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_nodes_window_filter_on_non_temporal_property() { - let filter1 = NodeFilter.window(1, 2).property("p1").eq("shivam_kapoor"); - let filter2 = NodeFilter - .window(100, 200) - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter1.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter1.clone(), - &expected_results, - TestVariants::All, - ); - - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = NodeFilter - .window(100, 200) - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_filter_nodes_for_gt_node_id() { + let filter = NodeFilter::id().gt(2); + let expected_results = vec!["3", "4"]; - #[test] - fn test_nodes_window_filter_any_all_over_window() { - let filter = NodeFilter - .window(3, 5) - .property("p20") - .temporal() - .any() - .eq("Gold_boat"); - - let expected_results = vec!["4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .window(3, 5) - .property("p20") - .temporal() - .all() - .eq("Gold_boat"); - - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_window_filter_and() { - // Filters both node 1 and 3 - let filter1 = NodeFilter - .window(1, 4) - .property("p10") - .temporal() - .any() - .eq("Paper_airplane"); - - // Filters only node 3 - let filter2 = NodeFilter - .window(3, 6) - .property("p2") - .temporal() - .sum() - .eq(6u64); - - let filter = filter1.and(filter2); - - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_at_filter() { - // Only time=2 contributes; node 2 has p2=2 at t=2 - let filter = NodeFilter.at(2).property("p2").temporal().sum().eq(2u64); + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + #[test] + fn test_filter_nodes_for_ge_node_id() { + let filter = NodeFilter::id().ge(2); + let expected_results = vec!["2", "3", "4"]; - // Only time=3 contributes; node 3 has p2=6 at t=3 - let filter = NodeFilter.at(3).property("p2").temporal().sum().eq(6u64); + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_starts_with_node_id() { + let filter = NodeFilter::id().starts_with("France"); + let expected_results = vec!["France Paris"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_nodes_after_filter() { - // after(2) means t >= 3 - let filter = NodeFilter.after(2).property("p2").temporal().sum().ge(6u64); + #[test] + fn test_filter_nodes_for_ends_with_node_id() { + let filter = NodeFilter::id().ends_with("wo"); + let expected_results = vec!["Two"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_contains_node_id() { + let filter = NodeFilter::id().contains("o"); + let expected_results = vec!["London", "Tokyo", "Two"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_nodes_before_filter() { - // before(3) means t <= 2 - let filter = NodeFilter - .before(3) - .property("p2") - .temporal() - .sum() - .eq(2u64); - - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - // And node 3 shouldn't match, because its p2=6 lives at t=3. - let filter = NodeFilter - .before(3) - .property("p2") - .temporal() - .sum() - .eq(6u64); - - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_not_contains_node_id() { + let filter = NodeFilter::id().not_contains("o"); + let expected_results = vec!["France Paris"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_nodes_latest_filter() { - // At latest time (currently t=4), only node 4 has p5=12 - let filter = NodeFilter.latest().property("p5").eq(12u64); + #[test] + fn test_filter_nodes_for_is_in_node_id_str() { + let filter = NodeFilter::id().is_in(vec!["London", "Tokyo"]); + let expected_results = vec!["London", "Tokyo"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - let expected_results = vec!["4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_is_not_in_node_id_str() { + let filter = NodeFilter::id().is_not_in(vec!["London", "Tokyo"]); + let expected_results = vec!["France Paris", "Two"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_nodes_snapshot_at_semantics_event_graph() { - let t = 2; + #[test] + fn test_is_active_node_window() { + let filter = NodeFilter.window(1, 10).is_active(); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter_snapshot = NodeFilter.snapshot_at(t).property("p2").eq(2u64); + #[test] + fn test_is_active_node_window_not() { + let filter = NodeFilter + .window(1, 10) + .is_active() + .try_as_composite_node_filter() + .unwrap(); + let filter = CompositeNodeFilter::Not(Box::new(filter)); + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter_before = NodeFilter.before(t + 1).property("p2").eq(2u64); + #[test] + fn test_is_active_node_latest() { + let filter = NodeFilter.latest().is_active(); + let expected_results = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } - let expected_results = vec!["2"]; + #[test] + fn test_filter_by_column() { + let graph = Graph::new(); + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 3, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 5, NO_PROPS, None, None).unwrap(); + + let mask = alternating_mask(&graph); + let expected_nodes: Vec<_> = graph + .nodes() + .name() + .iter_values() + .skip(1) + .step_by(2) + .collect(); + + let filtered = graph + .filter(NodeFilter::by_column(&mask, "bool_col").unwrap()) + .unwrap(); - // snapshot_at - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot, - &expected_results, - TestVariants::EventOnly, - ); - - // before(t+1) - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_before.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_before, - &expected_results, - TestVariants::EventOnly, - ); - } + let names = filtered + .nodes() + .iter() + .map(|n| n.id().to_string()) + .collect::>(); - #[test] - fn test_nodes_snapshot_at_semantics_persistent_graph() { - let t = 2; + assert_eq!(names, expected_nodes); - let filter_snapshot = NodeFilter.snapshot_at(t).property("p2").eq(2u64); + let filtered = graph + .nodes() + .select(NodeFilter::by_column(&mask, "bool_col").unwrap()) + .unwrap(); - let filter_at = NodeFilter.at(t).property("p2").eq(2u64); + let names = filtered + .iter() + .map(|n| n.id().to_string()) + .collect::>(); - let expected_results = vec!["2"]; + assert_eq!(names, expected_nodes); + } - // snapshot_at - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot, - &expected_results, - TestVariants::PersistentOnly, - ); - - // at(t) - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_at.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_at, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_is_active_node_snapshot_at() { + let filter = NodeFilter.snapshot_at(2).is_active(); + let expected_results = vec!["2"]; + assert_select_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } } - #[test] - fn test_nodes_snapshot_latest_semantics_event_graph() { - let filter_snapshot_latest = NodeFilter - .snapshot_latest() - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let filter_noop = NodeFilter.property("p2").temporal().sum().ge(2u64); + #[cfg(test)] + mod test_node_property_filter { + use crate::test::{init_nodes_graph, IdentityGraphTransformer}; + use raphtory::db::graph::{ + assertions::{assert_filter_nodes_results, assert_search_nodes_results, TestVariants}, + views::filter::model::{ + graph_filter::GraphFilter, + node_filter::NodeFilter, + not_filter::NotFilter, + property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, + windowed_filter::Windowed, + ComposableFilter, PropertyFilterFactory, TemporalPropertyFilterFactory, + ViewWrapOps, + }, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use std::vec; - // From your earlier window test, "2" and "3" are the ones with p2 values across time. - // If your underlying dataset changes, adjust this accordingly. - let expected_results = vec!["2", "3"]; + #[test] + fn test_exact_match() { + // let filter = NodeFilter.degree > 5 + let filter = NodeFilter.property("p10").eq("Paper_airplane"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - // snapshot_latest - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot_latest.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot_latest, - &expected_results, - TestVariants::EventOnly, - ); - - // no-op baseline - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_noop.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_noop, - &expected_results, - TestVariants::EventOnly, - ); - } + let filter = NodeFilter.property("p10").eq(""); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_nodes_snapshot_latest_semantics_persistent_graph() { - let filter_snapshot_latest = NodeFilter - .snapshot_latest() - .property("p1") - .eq("shivam_kapoor"); + #[test] + fn test_not_exact_match() { + let filter = NodeFilter.property("p10").eq("Paper"); + let expected_results: Vec<&str> = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - let filter_latest = NodeFilter.latest().property("p1").eq("shivam_kapoor"); + #[test] + fn test_filter_nodes_for_property_eq() { + let filter = NodeFilter.property("p2").eq(2u64); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - let expected_results = vec!["1"]; + let filter = NodeFilter.property("p30").temporal().first().eq("Old_boat"); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - // snapshot_latest - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot_latest.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot_latest, - &expected_results, - TestVariants::PersistentOnly, - ); - - // latest - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_latest.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_latest, - &expected_results, - TestVariants::PersistentOnly, - ); - } + let filter = NodeFilter.property("p20").temporal().all().eq("Gold_ship"); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_nodes_layer_filter() { - let filter = NodeFilter - .layer("_default") - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_nodes_layer_then_window_ordering() { - // In layer "fire_nation" within window [1,4), node "1" matches p1 == "shivam_kapoor". - let filter = NodeFilter - .layer("fire_nation") - .window(1, 4) - .property("p1") - .eq("shivam_kapoor"); + #[test] + fn test_filter_nodes_for_property_ne() { + let filter = NodeFilter.property("p2").ne(2u64); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - let expected_results = vec!["1"]; + let filter = NodeFilter.property("p30").temporal().first().ne("Old_boat"); + let expected_results = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter.property("p1").temporal().all().ne("Gold_ship"); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_nodes_window_then_layer_ordering() { - // Same semantics as above, but reversed chaining order. - let filter = NodeFilter - .window(1, 4) - .layer("fire_nation") - .property("p1") - .eq("shivam_kapoor"); + #[test] + fn test_filter_nodes_for_property_lt() { + let filter = NodeFilter.property("p2").lt(10u64); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - let expected_results = vec!["1"]; + let filter = NodeFilter.property("p40").temporal().first().lt(10u64); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter.property("p9").temporal().all().lt(10u64); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_graph_filter_window() { - let filter: Windowed = GraphFilter.window(1, 2); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.window(1, 3); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.window(4, 6); - let expected_results = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = GraphFilter.window(4, 6); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_filter_nodes_for_property_le() { + let filter = NodeFilter.property("p2").le(6u64); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_graph_filter_layer() { - let filter = GraphFilter.layer("fire_nation"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.layer("air_nomads"); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter.property("p9").temporal().first().le(10u64); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_graph_filter_window_then_layer() { - let filter = GraphFilter.window(1, 3).layer("fire_nation"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.window(4, 4).layer("air_nomads"); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter.property("p2").temporal().all().le(10u64); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_graph_filter_layer_then_window() { - let filter = GraphFilter.layer("fire_nation").window(1, 3); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_property_gt() { + let filter = NodeFilter.property("p2").gt(2u64); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_graph_filter_at() { - let filter = GraphFilter.at(1); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.at(2); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = GraphFilter.at(2); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = GraphFilter.at(3); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter.property("p40").temporal().first().gt(5u64); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_graph_filter_after() { - let filter = GraphFilter.after(3); - let expected_results = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = GraphFilter.after(3); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + let filter = NodeFilter.property("p9").temporal().all().gt(1u64); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_graph_filter_before() { - let filter = GraphFilter.before(2); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.before(3); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_nodes_for_property_ge() { + let filter = NodeFilter.property("p2").ge(2u64); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_graph_filter_snapshot_at() { - let filter = GraphFilter.snapshot_at(1); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.snapshot_at(3); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.snapshot_at(4); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter.property("p40").temporal().first().ge(5u64); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_graph_filter_snapshot_latest() { - let filter = GraphFilter.snapshot_latest(); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = NodeFilter.property("p40").temporal().all().ge(5u64); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_graph_filter_latest() { - let filter = GraphFilter.latest(); - let expected_results = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = GraphFilter.latest(); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } -} + #[test] + fn test_filter_nodes_for_property_in() { + let filter = NodeFilter.property("p2").is_in(vec![Prop::U64(6)]); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); -#[cfg(test)] -mod test_node_composite_filter { - use raphtory_api::core::Direction; + let filter = NodeFilter + .property("p2") + .is_in(vec![Prop::U64(2), Prop::U64(6)]); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - use crate::{init_edges_graph, init_nodes_graph, IdentityGraphTransformer}; - use raphtory::{ - db::graph::{ - assertions::{ - assert_filter_neighbours_results, assert_filter_nodes_results, - assert_search_nodes_results, TestVariants, + let filter = NodeFilter + .property("p40") + .temporal() + .first() + .is_in(vec![Prop::U64(5)]); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p2") + .temporal() + .any() + .is_in(vec![Prop::U64(2)]); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_not_in() { + let filter = NodeFilter.property("p2").is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p2") + .temporal() + .all() + .is_not_in(vec![Prop::U64(2)]); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_is_some() { + let filter = NodeFilter.property("p2").is_some(); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").is_some(); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_is_none() { + let filter = NodeFilter.property("p2").is_none(); + let expected_results = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").is_none(); + let expected_results = vec!["3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_starts_with() { + let filter = NodeFilter.property("p10").starts_with("Pa"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .any() + .starts_with("Pap"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .starts_with("Pape"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .starts_with("Yohan"); + let expected_results: Vec<&str> = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p30") + .temporal() + .first() + .starts_with("Gold"); + let expected_results: Vec<&str> = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p20") + .temporal() + .all() + .starts_with("Gold"); + let expected_results: Vec<&str> = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_ends_with() { + let filter = NodeFilter.property("p10").ends_with("lane"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .any() + .ends_with("ship"); + let expected_results: Vec<&str> = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .ends_with("ane"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .ends_with("Jerry"); + let expected_results: Vec<&str> = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p20") + .temporal() + .first() + .ends_with("boat"); + let expected_results: Vec<&str> = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p20") + .temporal() + .all() + .ends_with("ship"); + let expected_results: Vec<&str> = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_contains() { + let filter = NodeFilter.property("p10").contains("Paper"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .any() + .contains("Paper"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .contains("Paper"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p30") + .temporal() + .first() + .contains("Old"); + let expected_results: Vec<&str> = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p30").temporal().all().contains("Gold"); + let expected_results: Vec<&str> = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_contains_not() { + let filter = NodeFilter.property("p10").not_contains("ship"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .any() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p30") + .temporal() + .first() + .not_contains("Old"); + let expected_results: Vec<&str> = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p30") + .temporal() + .all() + .not_contains("boat"); + let expected_results: Vec<&str> = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_not_property() { + let filter = NotFilter(NodeFilter.property("p10").contains("Paper")); + let expected_results: Vec<&str> = vec!["4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p10").contains("Paper").not(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_sum() { + let filter = NodeFilter.property("p9").temporal().sum().eq(15u64); + let expected_results: Vec<&str> = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_avg() { + let filter = NodeFilter.property("p2").temporal().avg().le(10f64); + let expected_results: Vec<&str> = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_min() { + let filter = NodeFilter.property("p40").temporal().min().is_in(vec![ + Prop::U64(5), + Prop::U64(10), + Prop::U64(20), + ]); + let expected_results: Vec<&str> = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_max() { + let filter = NodeFilter.property("p3").temporal().max().is_not_in(vec![ + Prop::U64(5), + Prop::U64(10), + Prop::U64(20), + ]); + let expected_results: Vec<&str> = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_len() { + let filter = NodeFilter.property("p2").temporal().len().le(5u64); + let expected_results: Vec<&str> = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_window_filter() { + let filter = NodeFilter + .window(1, 3) + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + // Wider window includes node 3 + let filter = NodeFilter + .window(1, 5) + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_window_filter_on_non_temporal_property() { + let filter1 = NodeFilter.window(1, 2).property("p1").eq("shivam_kapoor"); + let filter2 = NodeFilter + .window(100, 200) + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter1.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter1.clone(), + &expected_results, + TestVariants::All, + ); + + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter + .window(100, 200) + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_window_filter_any_all_over_window() { + let filter = NodeFilter + .window(3, 5) + .property("p20") + .temporal() + .any() + .eq("Gold_boat"); + + let expected_results = vec!["4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .window(3, 5) + .property("p20") + .temporal() + .all() + .eq("Gold_boat"); + + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_window_filter_and() { + // Filters both node 1 and 3 + let filter1 = NodeFilter + .window(1, 4) + .property("p10") + .temporal() + .any() + .eq("Paper_airplane"); + + // Filters only node 3 + let filter2 = NodeFilter + .window(3, 6) + .property("p2") + .temporal() + .sum() + .eq(6u64); + + let filter = filter1.and(filter2); + + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_at_filter() { + // Only time=2 contributes; node 2 has p2=2 at t=2 + let filter = NodeFilter.at(2).property("p2").temporal().sum().eq(2u64); + + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + // Only time=3 contributes; node 3 has p2=6 at t=3 + let filter = NodeFilter.at(3).property("p2").temporal().sum().eq(6u64); + + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_after_filter() { + // after(2) means t >= 3 + let filter = NodeFilter.after(2).property("p2").temporal().sum().ge(6u64); + + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_before_filter() { + // before(3) means t <= 2 + let filter = NodeFilter + .before(3) + .property("p2") + .temporal() + .sum() + .eq(2u64); + + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + // And node 3 shouldn't match, because its p2=6 lives at t=3. + let filter = NodeFilter + .before(3) + .property("p2") + .temporal() + .sum() + .eq(6u64); + + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_latest_filter() { + // At latest time (currently t=4), only node 4 has p5=12 + let filter = NodeFilter.latest().property("p5").eq(12u64); + + let expected_results = vec!["4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_snapshot_at_semantics_event_graph() { + let t = 2; + + let filter_snapshot = NodeFilter.snapshot_at(t).property("p2").eq(2u64); + + let filter_before = NodeFilter.before(t + 1).property("p2").eq(2u64); + + let expected_results = vec!["2"]; + + // snapshot_at + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot, + &expected_results, + TestVariants::EventOnly, + ); + + // before(t+1) + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_before.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_before, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_snapshot_at_semantics_persistent_graph() { + let t = 2; + + let filter_snapshot = NodeFilter.snapshot_at(t).property("p2").eq(2u64); + + let filter_at = NodeFilter.at(t).property("p2").eq(2u64); + + let expected_results = vec!["2"]; + + // snapshot_at + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot, + &expected_results, + TestVariants::PersistentOnly, + ); + + // at(t) + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_at.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_at, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_snapshot_latest_semantics_event_graph() { + let filter_snapshot_latest = NodeFilter + .snapshot_latest() + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let filter_noop = NodeFilter.property("p2").temporal().sum().ge(2u64); + + // From your earlier window test, "2" and "3" are the ones with p2 values across time. + // If your underlying dataset changes, adjust this accordingly. + let expected_results = vec!["2", "3"]; + + // snapshot_latest + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot_latest.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot_latest, + &expected_results, + TestVariants::EventOnly, + ); + + // no-op baseline + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_noop.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_noop, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_snapshot_latest_semantics_persistent_graph() { + let filter_snapshot_latest = NodeFilter + .snapshot_latest() + .property("p1") + .eq("shivam_kapoor"); + + let filter_latest = NodeFilter.latest().property("p1").eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + + // snapshot_latest + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot_latest.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot_latest, + &expected_results, + TestVariants::PersistentOnly, + ); + + // latest + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_latest.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_latest, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_nodes_layer_filter() { + let filter = NodeFilter + .layer("_default") + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_nodes_layer_then_window_ordering() { + // In layer "fire_nation" within window [1,4), node "1" matches p1 == "shivam_kapoor". + let filter = NodeFilter + .layer("fire_nation") + .window(1, 4) + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_nodes_window_then_layer_ordering() { + // Same semantics as above, but reversed chaining order. + let filter = NodeFilter + .window(1, 4) + .layer("fire_nation") + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_window() { + let filter: Windowed = GraphFilter.window(1, 2); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.window(1, 3); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.window(4, 6); + let expected_results = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = GraphFilter.window(4, 6); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_graph_filter_layer() { + let filter = GraphFilter.layer("fire_nation"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.layer("air_nomads"); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_graph_filter_window_then_layer() { + let filter = GraphFilter.window(1, 3).layer("fire_nation"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.window(4, 4).layer("air_nomads"); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_graph_filter_layer_then_window() { + let filter = GraphFilter.layer("fire_nation").window(1, 3); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_at() { + let filter = GraphFilter.at(1); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.at(2); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = GraphFilter.at(2); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = GraphFilter.at(3); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_after() { + let filter = GraphFilter.after(3); + let expected_results = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = GraphFilter.after(3); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_graph_filter_before() { + let filter = GraphFilter.before(2); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.before(3); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_snapshot_at() { + let filter = GraphFilter.snapshot_at(1); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.snapshot_at(3); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.snapshot_at(4); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_snapshot_latest() { + let filter = GraphFilter.snapshot_latest(); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_latest() { + let filter = GraphFilter.latest(); + let expected_results = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = GraphFilter.latest(); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + } + + #[cfg(test)] + mod test_node_composite_filter { + use raphtory_api::core::Direction; + + use crate::test::{init_edges_graph, init_nodes_graph, IdentityGraphTransformer}; + use raphtory::{ + db::graph::{ + assertions::{ + assert_filter_neighbours_results, assert_filter_nodes_results, + assert_search_nodes_results, TestVariants, + }, + views::filter::model::{ + node_filter::ops::NodeFilterOps, property_filter::ops::PropertyFilterOps, + ComposableFilter, PropertyFilterFactory, TryAsCompositeFilter, + }, }, - views::filter::model::{ - node_filter::ops::NodeFilterOps, property_filter::ops::PropertyFilterOps, - ComposableFilter, PropertyFilterFactory, TryAsCompositeFilter, + prelude::NodeFilter, + }; + + #[test] + fn test_filter_nodes_by_props_added_at_different_times() { + let filter = NodeFilter + .property("p4") + .eq("pometry") + .and(NodeFilter.property("p5").eq(12u64)); + let expected_results = vec!["4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_unique_results_from_composite_filters() { + let filter = NodeFilter + .property("p2") + .ge(2u64) + .and(NodeFilter.property("p2").ge(1u64)); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p2") + .ge(2u64) + .or(NodeFilter.property("p2").ge(5u64)); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_composite_filter_nodes() { + let filter = NodeFilter + .property("p2") + .eq(2u64) + .and(NodeFilter.property("p1").eq("kapoor")); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p2") + .eq(2u64) + .or(NodeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p1").eq("pometry").or(NodeFilter + .property("p2") + .eq(6u64) + .and(NodeFilter.property("p3").eq(1u64))); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::node_type() + .eq("fire_nation") + .and(NodeFilter.property("p1").eq("prop1")); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p9") + .eq(5u64) + .and(NodeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::node_type() + .eq("fire_nation") + .and(NodeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::name() + .eq("2") + .and(NodeFilter.property("p2").eq(2u64)); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::name() + .eq("2") + .and(NodeFilter.property("p2").eq(2u64)) + .or(NodeFilter.property("p9").eq(5u64)); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_not_composite_filter_nodes() { + let filter = NodeFilter::name() + .eq("2") + .and(NodeFilter.property("p2").eq(2u64)) + .or(NodeFilter.property("p9").eq(5u64)) + .not(); + let expected_results = vec!["3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::name() + .eq("2") + .not() + .and(NodeFilter.property("p2").eq(2u64)) + .or(NodeFilter.property("p9").eq(5u64)); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_out_neighbours_filter() { + let filter = NodeFilter::name() + .eq("2") + .and(NodeFilter.property("p2").eq(2u64)); + let expected_results = vec!["2"]; + assert_filter_neighbours_results( + |graph| init_edges_graph(init_nodes_graph(graph)), + IdentityGraphTransformer, + "1", + Direction::OUT, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_in_neighbours_filter() { + let filter = NodeFilter.property("p9").ge(1u64); + let expected_results = vec!["1"]; + assert_filter_neighbours_results( + |graph| init_edges_graph(init_nodes_graph(graph)), + IdentityGraphTransformer, + "2", + Direction::IN, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_neighbours_filter() { + let filter = NodeFilter.property("p10").contains("Paper"); + let expected_results = vec!["1", "3"]; + assert_filter_neighbours_results( + |graph| init_edges_graph(init_nodes_graph(graph)), + IdentityGraphTransformer, + "2", + Direction::BOTH, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + } + + #[cfg(test)] + mod test_node_property_filter_agg { + use crate::test::IdentityGraphTransformer; + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::{ + assertions::{ + assert_filter_nodes_err, assert_filter_nodes_results, + assert_search_nodes_results, TestVariants::All, + }, + views::filter::{ + model::{ + node_filter::NodeFilter, + property_filter::ops::{ + ElemQualifierOps, ListAggOps, PropertyFilterOps, + }, + PropertyFilterFactory, TemporalPropertyFilterFactory, + TryAsCompositeFilter, + }, + CreateFilter, + }, + }, }, - }, - prelude::NodeFilter, - }; + prelude::{AdditionOps, GraphViewOps, PropertyAdditionOps}, + }; + use raphtory_api::core::{ + entities::properties::prop::{IntoProp, Prop}, + storage::arc_str::ArcStr, + }; + use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, + }; + + fn list_u8(xs: &[u8]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::U8)) + } + fn list_u16(xs: &[u16]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::U16)) + } + fn list_u32(xs: &[u32]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::U32)) + } + fn list_u64(xs: &[u64]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::U64)) + } + fn list_i32(xs: &[i32]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::I32)) + } + fn list_i64(xs: &[i64]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::I64)) + } + fn list_f32(xs: &[f32]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::F32)) + } + fn list_f64(xs: &[f64]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::F64)) + } + fn list_str(xs: &[&str]) -> Prop { + Prop::list(xs.iter().map(|s| Prop::Str(ArcStr::from(*s)))) + } + fn list_bool(xs: &[bool]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::Bool)) + } + + #[inline] + fn list(v: Vec) -> Prop { + Prop::List(v.into()) + } + + pub fn init_nodes_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes: [(i64, &str, Vec<(&str, Prop)>); 12] = [ + ( + 1, + "n1", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u8s_max", list_u8(&[u8::MAX, u8::MAX])), // min: u8::MAX, max: u8::MAX, sum: 510 + ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u16s_max", list_u16(&[u16::MAX, u16::MAX])), // min: u16::MAX, max: u16::MAX, sum: 131070 + ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u32s_max", list_u32(&[u32::MAX, u32::MAX])), // min: 1, max: 3, sum: 8589934590 + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u64s_max", list_u64(&[u64::MAX, u64::MAX])), // min: 1, max: 3, sum: OVERFLOW + ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i64s", list_i64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 + ( + "nested_list", + list(vec![ + list(vec![ + list(vec![ + list(vec![50.0.into_prop(), 40.0.into_prop()]), + list(vec![60.0.into_prop()]), + ]), + list(vec![list(vec![46.0.into_prop()])]), + ]), + list(vec![list(vec![list(vec![90.0.into_prop()])])]), + ]), + ), + ], + ), + ( + 2, + "n1", + vec![ + ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 + ("p_bools", list_bool(&[true, true])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_u16s", list_u16(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_u32s", list_u32(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_i32s", list_i32(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_i64s", list_i64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5, 4.5])), // min: 1.0, max: 4.5, sum: 11.0, avg: 2.75, len: 4 + ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 + ], + ), + ( + 1, + "n2", + vec![ + ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 + ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 + ("p_bools", list_bool(&[false, false])), + ], + ), + ( + 2, + "n2", + vec![ + ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 + ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 + ], + ), + ( + 1, + "n3", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 1, 4])), // min: 1, max: 4, sum: 6, avg: 2.0, len: 3 + ("p_u16s", list_u16(&[1, 0, 5])), // min: 0, max: 5, sum: 6, avg: 2.0, len: 3 + ("p_u32s", list_u32(&[2, 2, 2])), // min: 2, max: 2, sum: 6, avg: 2.0, len: 3 + ("p_u64s", list_u64(&[0, 3, 3])), // min: 0, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i32s", list_i32(&[-1, 4, 3])), // min: -1, max: 4, sum: 6, avg: 2.0, len: 3 + ("p_i64s", list_i64(&[0, 3, -3])), // min: -3, max: 3, sum: 0, avg: 0.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.5, 3.0])), // min: 1.0, max: 3.0, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_f64s", list_f64(&[30.0, 60.0])), // min: 30.0, max: 60.0, sum: 90.0, avg: 45.0, len: 2 + ( + "nested_list", + list(vec![ + list(vec![ + list(vec![ + list(vec![50.0.into_prop(), 40.0.into_prop()]), + list(vec![60.0.into_prop()]), + ]), + list(vec![list(vec![46.0.into_prop()])]), + ]), + list(vec![list(vec![list(vec![90.0.into_prop()])])]), + ]), + ), + ], + ), + ( + 2, + "n3", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i64s", list_i64(&[1, 2, -3])), // min: -3, max: 2, sum: 0, avg: 0.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 + ( + "nested_list", + list(vec![ + list(vec![ + list(vec![ + list(vec![50.0.into_prop(), 40.0.into_prop()]), + list(vec![60.0.into_prop()]), + ]), + list(vec![list(vec![46.0.into_prop()])]), + ]), + list(vec![list(vec![list(vec![90.0.into_prop()])])]), + ]), + ), + ], + ), + ( + 1, + "n4", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_bools_all", list_bool(&[true, true])), + ], + ), + ( + 2, + "n4", + vec![ + ("p_strs", list_str(&["x", "y", "z"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[false, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u64s", list_u64(&[10, 20, 30])), // min: 10, max: 30, sum: 60, avg: 20.0, len: 3 + ("p_i32s", list_i32(&[10, 20, 30])), // min: 10, max: 30, sum: 60, avg: 20.0, len: 3 + ("p_f32s", list_f32(&[10.0, 20.0, 30.0])), // min: 10.0, max: 30.0, sum: 60.0, avg: 20.0, len: 3 + ("p_bools_all", list_bool(&[true, true])), + ], + ), + ( + 2, + "n5", + vec![ + ("p_u64s", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 + ("p_u64s_max", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 + ("p_u64s_min", list_u64(&[u64::MIN, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 + ("p_i64s", list_i64(&[i64::MAX, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 + ("p_i64s_max", list_i64(&[i64::MAX, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 + ("p_i64s_min", list_i64(&[i64::MIN, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 + ], + ), + ( + 2, + "n6", + vec![ + ("p_i32s", list_i32(&[-2, 1, 3])), // min: -2, max: 3, sum: 2, avg: 0.6666666666666666, len: 3 + ], + ), + ( + 1, + "n7", + vec![ + ("p_u64s", list_u64(&[])), // min: None, max: None, sum: None, avg: None, len: 0 + ], + ), + ( + 2, + "n10", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i64s", list_i64(&[1, 2, -3])), // min: -3, max: 2, sum: 0, avg: 0.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 + ("p_bools_all", list_bool(&[true, true])), + ], + ), + ]; + + for (t, id, props) in nodes { + graph.add_node(t, id, props, None, None).unwrap(); + } + + let metadata: [(&str, Vec<(&str, Prop)>); 8] = [ + ( + "n1", + vec![ + ("p_u8s", list_u8(&[2, 9])), // min: 2, max: 9, sum: 11, avg: 5.5, len: 2 + ("p_u16s", list_u16(&[3, 5])), // min: 3, max: 5, sum: 8, avg: 4.0, len: 2 + ("p_u32s", list_u32(&[4, 9])), // min: 4, max: 9, sum: 13, avg: 6.5, len: 2 + ], + ), + ( + "n2", + vec![ + ("p_u64s", list_u64(&[2, 3, 7])), // min: 2, max: 7, sum: 12, avg: 4.0, len: 3 + ], + ), + ( + "n3", + vec![ + ("p_i32s", list_i32(&[10, 2, -3])), // min: -3, max: 10, sum: 9, avg: 3.0, len: 3 + ("p_i64s", list_i64(&[1, 12, 3, 4])), // min: 1, max: 12, sum: 20, avg: 5.0, len: 4 + ], + ), + ( + "n4", + vec![ + ("p_f32s", list_f32(&[1.5, 2.5])), // min: 1.5, max: 2.5, sum: 4.0, avg: 2.0, len: 2 + ("p_f64s", list_f64(&[0.5, 1.5])), // min: 0.5, max: 1.5, sum: 2.0, avg: 1.0, len: 2 + ], + ), + ( + "n5", + vec![ + ("p_strs", list_str(&["m1", "m2", "m3"])), // min: None, max: None, sum: None, avg: None, len: 3 + ], + ), + ( + "n6", + vec![ + ("p_u64s", list_u64(&[])), // min: None, max: None, sum: None, avg: None, len: 0 + ("p_strs", list_str(&["a", "a"])), + ], + ), + ( + "n7", + vec![ + ("p_u64s", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: ~9.22e18, len: 2 + ("p_strs", list_str(&["a"])), + ], + ), + ( + "n10", + vec![ + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ], + ), + ]; + + for (node_id, md) in metadata { + graph.node(node_id).unwrap().add_metadata(md).unwrap(); + } + + graph + } + + fn apply_assertion( + filter: impl TryAsCompositeFilter + CreateFilter + Clone, + expected: &[&str], + ) { + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected, + All, + ); + + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected, + All, + ); + } + + fn apply_assertion_err( + filter: impl TryAsCompositeFilter + CreateFilter + Clone, + expected: &str, + ) { + assert_filter_nodes_err( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected, + All, + ); + + // assert_search_nodes_err( + // init_nodes_graph, + // IdentityGraphTransformer, + // filter, + // expected, + // All, + // ); + } + + // ------ Property: SUM ---- + #[test] + fn test_node_property_sum_u8s() { + let filter = NodeFilter.property("p_u8s").sum().eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_sum_u16s() { + let filter = NodeFilter.property("p_u16s").sum().eq(Prop::U64(6)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_sum_u32s() { + let filter = NodeFilter.property("p_u32s").sum().eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_filter_nodes_by_props_added_at_different_times() { - let filter = NodeFilter - .property("p4") - .eq("pometry") - .and(NodeFilter.property("p5").eq(12u64)); - let expected_results = vec!["4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_node_property_sum_u64s() { + let filter = NodeFilter.property("p_u64s").sum().eq(Prop::U64(6)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_unique_results_from_composite_filters() { - let filter = NodeFilter - .property("p2") - .ge(2u64) - .and(NodeFilter.property("p2").ge(1u64)); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .ge(2u64) - .or(NodeFilter.property("p2").ge(5u64)); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_node_property_sum_i32s() { + let filter = NodeFilter.property("p_i32s").sum().eq(Prop::I64(2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_composite_filter_nodes() { - let filter = NodeFilter - .property("p2") - .eq(2u64) - .and(NodeFilter.property("p1").eq("kapoor")); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .eq(2u64) - .or(NodeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p1").eq("pometry").or(NodeFilter - .property("p2") - .eq(6u64) - .and(NodeFilter.property("p3").eq(1u64))); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type() - .eq("fire_nation") - .and(NodeFilter.property("p1").eq("prop1")); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p9") - .eq(5u64) - .and(NodeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type() - .eq("fire_nation") - .and(NodeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name() - .eq("2") - .and(NodeFilter.property("p2").eq(2u64)); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name() - .eq("2") - .and(NodeFilter.property("p2").eq(2u64)) - .or(NodeFilter.property("p9").eq(5u64)); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_node_property_sum_i64s() { + let filter = NodeFilter.property("p_i64s").sum().eq(Prop::I64(0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_not_composite_filter_nodes() { - let filter = NodeFilter::name() - .eq("2") - .and(NodeFilter.property("p2").eq(2u64)) - .or(NodeFilter.property("p9").eq(5u64)) - .not(); - let expected_results = vec!["3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name() - .eq("2") - .not() - .and(NodeFilter.property("p2").eq(2u64)) - .or(NodeFilter.property("p9").eq(5u64)); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_node_property_sum_f32s() { + let filter = NodeFilter.property("p_f32s").sum().eq(Prop::F64(6.5)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_out_neighbours_filter() { - let filter = NodeFilter::name() - .eq("2") - .and(NodeFilter.property("p2").eq(2u64)); - let expected_results = vec!["2"]; - assert_filter_neighbours_results( - |graph| init_edges_graph(init_nodes_graph(graph)), - IdentityGraphTransformer, - "1", - Direction::OUT, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_node_property_sum_f64s() { + let filter = NodeFilter.property("p_f64s").sum().eq(Prop::F64(120.0)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + // ------ Property: AVG ---- + #[test] + fn test_node_property_avg_u8s() { + let filter = NodeFilter.property("p_u8s").avg().eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_u16s() { + let filter = NodeFilter.property("p_u16s").avg().eq(Prop::F64(2.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_u32s() { + let filter = NodeFilter.property("p_u32s").avg().eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_u64s() { + let filter = NodeFilter.property("p_u64s").avg().eq(Prop::F64(2.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .avg() + .eq(Prop::F64(0.6666666666666666)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_i64s() { + let filter = NodeFilter.property("p_i64s").avg().eq(Prop::F64(0.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .avg() + .eq(Prop::F64(2.1666666666666665)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_f64s() { + let filter = NodeFilter.property("p_f64s").avg().eq(Prop::F64(40.0)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + // ------ Property: LEN ------ + #[test] + fn test_node_property_len_u8s() { + let filter = NodeFilter.property("p_u8s").len().eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_u16s() { + let filter = NodeFilter.property("p_u16s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_u32s() { + let filter = NodeFilter.property("p_u32s").len().eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_u64s() { + let filter = NodeFilter.property("p_u64s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_i32s() { + let filter = NodeFilter.property("p_i32s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_i64s() { + let filter = NodeFilter.property("p_i64s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_f32s() { + let filter = NodeFilter.property("p_f32s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_f64s() { + let filter = NodeFilter.property("p_f64s").len().eq(Prop::U64(3)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_strs() { + let filter = NodeFilter.property("p_strs").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + // ------ Property: MIN ------ + #[test] + fn test_node_property_min_u8s() { + let filter = NodeFilter.property("p_u8s").min().eq(Prop::U8(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_u16s() { + let filter = NodeFilter.property("p_u16s").min().eq(Prop::U16(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_u32s() { + let filter = NodeFilter.property("p_u32s").min().eq(Prop::U32(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_u64s() { + let filter = NodeFilter.property("p_u64s").min().eq(Prop::U64(1)); + let expected = vec!["n1", "n10", "n2", "n3", "n5"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_i32s() { + let filter = NodeFilter.property("p_i32s").min().eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_i64s() { + let filter = NodeFilter.property("p_i64s").min().eq(Prop::I64(-3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_f32s() { + let filter = NodeFilter.property("p_f32s").min().eq(Prop::F32(10.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_f64s() { + let filter = NodeFilter.property("p_f64s").min().eq(Prop::F64(40.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Property: MAX ------ + #[test] + fn test_node_property_max_u8s() { + let filter = NodeFilter.property("p_u8s").max().eq(Prop::U8(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_u16s() { + let filter = NodeFilter.property("p_u16s").max().eq(Prop::U16(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_u32s() { + let filter = NodeFilter.property("p_u32s").max().eq(Prop::U32(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_u64s() { + let filter = NodeFilter.property("p_u64s").max().eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_i32s() { + let filter = NodeFilter.property("p_i32s").max().eq(Prop::I32(3)); + let expected = vec!["n10", "n3", "n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_i64s() { + let filter = NodeFilter.property("p_i64s").max().eq(Prop::I64(2)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_f32s() { + let filter = NodeFilter.property("p_f32s").max().eq(Prop::F32(30.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_f64s() { + let filter = NodeFilter.property("p_f64s").max().eq(Prop::F64(50.0)); + let expected = vec!["n1", "n10", "n2", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: SUM ------ + #[test] + fn test_node_property_metadata_sum_u8s() { + let filter = NodeFilter.metadata("p_u8s").sum().eq(Prop::U64(11)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_u16s() { + let filter = NodeFilter.metadata("p_u16s").sum().eq(Prop::U64(8)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_u32s() { + let filter = NodeFilter.metadata("p_u32s").sum().eq(Prop::U64(13)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_u64s() { + let filter = NodeFilter.metadata("p_u64s").sum().eq(Prop::U64(12)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_i32s() { + let filter = NodeFilter.metadata("p_i32s").sum().eq(Prop::I64(9)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_i64s() { + let filter = NodeFilter.metadata("p_i64s").sum().eq(Prop::I64(20)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_f32s() { + let filter = NodeFilter.metadata("p_f32s").sum().eq(Prop::F64(4.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_f64s() { + let filter = NodeFilter.metadata("p_f64s").sum().eq(Prop::F64(2.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: AVG ------ + #[test] + fn test_node_property_metadata_avg_u8s() { + let filter = NodeFilter.metadata("p_u8s").avg().eq(Prop::F64(5.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_u16s() { + let filter = NodeFilter.metadata("p_u16s").avg().eq(Prop::F64(4.0)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_u32s() { + let filter = NodeFilter.metadata("p_u32s").avg().eq(Prop::F64(6.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_u64s() { + let filter = NodeFilter.metadata("p_u64s").avg().eq(Prop::F64(4.0)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_i32s() { + let filter = NodeFilter.metadata("p_i32s").avg().eq(Prop::F64(3.0)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_i64s() { + let filter = NodeFilter.metadata("p_i64s").avg().eq(Prop::F64(5.0)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_f32s() { + let filter = NodeFilter.metadata("p_f32s").avg().eq(Prop::F64(2.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_f64s() { + let filter = NodeFilter.metadata("p_f64s").avg().eq(Prop::F64(1.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: MIN ------ + #[test] + fn test_node_property_metadata_min_u8s() { + let filter = NodeFilter.metadata("p_u8s").min().eq(Prop::U8(2)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_u16s() { + let filter = NodeFilter.metadata("p_u16s").min().eq(Prop::U16(3)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_u32s() { + let filter = NodeFilter.metadata("p_u32s").min().eq(Prop::U32(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_u64s() { + let filter = NodeFilter.metadata("p_u64s").min().eq(Prop::U64(2)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_i32s() { + let filter = NodeFilter.metadata("p_i32s").min().eq(Prop::I32(-3)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_i64s() { + let filter = NodeFilter.metadata("p_i64s").min().eq(Prop::I64(1)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_f32s() { + let filter = NodeFilter.metadata("p_f32s").min().eq(Prop::F32(1.5)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_f64s() { + let filter = NodeFilter.metadata("p_f64s").min().eq(Prop::F64(0.5)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: MAX ------ + #[test] + fn test_node_property_metadata_max_u8s() { + let filter = NodeFilter.metadata("p_u8s").max().eq(Prop::U8(9)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_u16s() { + let filter = NodeFilter.metadata("p_u16s").max().eq(Prop::U16(5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_u32s() { + let filter = NodeFilter.metadata("p_u32s").max().eq(Prop::U32(9)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_u64s() { + let filter = NodeFilter.metadata("p_u64s").max().eq(Prop::U64(7)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_i32s() { + let filter = NodeFilter.metadata("p_i32s").max().eq(Prop::I32(10)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_i64s() { + let filter = NodeFilter.metadata("p_i64s").max().eq(Prop::I64(12)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_f32s() { + let filter = NodeFilter.metadata("p_f32s").max().eq(Prop::F32(2.5)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_f64s() { + let filter = NodeFilter.metadata("p_f64s").max().eq(Prop::F64(1.5)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: Len ------ + #[test] + fn test_node_property_metadata_len_u8s() { + let filter = NodeFilter.metadata("p_u8s").len().eq(Prop::U64(2)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_len_u16s() { + let filter = NodeFilter.metadata("p_u16s").len().eq(Prop::U64(2)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_in_neighbours_filter() { - let filter = NodeFilter.property("p9").ge(1u64); - let expected_results = vec!["1"]; - assert_filter_neighbours_results( - |graph| init_edges_graph(init_nodes_graph(graph)), - IdentityGraphTransformer, - "2", - Direction::IN, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_node_property_metadata_len_u32s() { + let filter = NodeFilter.metadata("p_u32s").len().eq(Prop::U64(2)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_neighbours_filter() { - let filter = NodeFilter.property("p10").contains("Paper"); - let expected_results = vec!["1", "3"]; - assert_filter_neighbours_results( - |graph| init_edges_graph(init_nodes_graph(graph)), - IdentityGraphTransformer, - "2", - Direction::BOTH, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } -} + #[test] + fn test_node_property_metadata_len_u64s() { + let filter = NodeFilter.metadata("p_u64s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n2"]; + apply_assertion(filter, &expected); + } -#[cfg(test)] -mod test_node_property_filter_agg { - use crate::IdentityGraphTransformer; - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::{ - assert_filter_nodes_err, assert_filter_nodes_results, - assert_search_nodes_results, TestVariants::All, - }, - views::filter::{ - model::{ - node_filter::NodeFilter, - property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, - PropertyFilterFactory, TemporalPropertyFilterFactory, TryAsCompositeFilter, - }, - CreateFilter, - }, - }, - }, - prelude::{AdditionOps, GraphViewOps, PropertyAdditionOps}, - }; - use raphtory_api::core::{ - entities::properties::prop::{IntoProp, Prop}, - storage::arc_str::ArcStr, - }; - use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, - }; + #[test] + fn test_node_property_metadata_len_i32s() { + let filter = NodeFilter.metadata("p_i32s").len().eq(Prop::U64(3)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } - fn list_u8(xs: &[u8]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::U8)) - } - fn list_u16(xs: &[u16]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::U16)) - } - fn list_u32(xs: &[u32]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::U32)) - } - fn list_u64(xs: &[u64]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::U64)) - } - fn list_i32(xs: &[i32]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::I32)) - } - fn list_i64(xs: &[i64]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::I64)) - } - fn list_f32(xs: &[f32]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::F32)) - } - fn list_f64(xs: &[f64]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::F64)) - } - fn list_str(xs: &[&str]) -> Prop { - Prop::list(xs.iter().map(|s| Prop::Str(ArcStr::from(*s)))) - } - fn list_bool(xs: &[bool]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::Bool)) - } + #[test] + fn test_node_property_metadata_len_i64s() { + let filter = NodeFilter.metadata("p_i64s").len().eq(Prop::U64(4)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } - #[inline] - fn list(v: Vec) -> Prop { - Prop::List(v.into()) - } + #[test] + fn test_node_property_metadata_len_f32s() { + let filter = NodeFilter.metadata("p_f32s").len().eq(Prop::U64(2)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - pub fn init_nodes_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let nodes: [(i64, &str, Vec<(&str, Prop)>); 12] = [ - ( - 1, - "n1", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u8s_max", list_u8(&[u8::MAX, u8::MAX])), // min: u8::MAX, max: u8::MAX, sum: 510 - ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u16s_max", list_u16(&[u16::MAX, u16::MAX])), // min: u16::MAX, max: u16::MAX, sum: 131070 - ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u32s_max", list_u32(&[u32::MAX, u32::MAX])), // min: 1, max: 3, sum: 8589934590 - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u64s_max", list_u64(&[u64::MAX, u64::MAX])), // min: 1, max: 3, sum: OVERFLOW - ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i64s", list_i64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 - ( - "nested_list", - list(vec![ - list(vec![ - list(vec![ - list(vec![50.0.into_prop(), 40.0.into_prop()]), - list(vec![60.0.into_prop()]), - ]), - list(vec![list(vec![46.0.into_prop()])]), - ]), - list(vec![list(vec![list(vec![90.0.into_prop()])])]), - ]), - ), - ], - ), - ( - 2, - "n1", - vec![ - ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 - ("p_bools", list_bool(&[true, true])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_u16s", list_u16(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_u32s", list_u32(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_i32s", list_i32(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_i64s", list_i64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5, 4.5])), // min: 1.0, max: 4.5, sum: 11.0, avg: 2.75, len: 4 - ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 - ], - ), - ( - 1, - "n2", - vec![ - ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 - ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 - ("p_bools", list_bool(&[false, false])), - ], - ), - ( - 2, - "n2", - vec![ - ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 - ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 - ], - ), - ( - 1, - "n3", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 1, 4])), // min: 1, max: 4, sum: 6, avg: 2.0, len: 3 - ("p_u16s", list_u16(&[1, 0, 5])), // min: 0, max: 5, sum: 6, avg: 2.0, len: 3 - ("p_u32s", list_u32(&[2, 2, 2])), // min: 2, max: 2, sum: 6, avg: 2.0, len: 3 - ("p_u64s", list_u64(&[0, 3, 3])), // min: 0, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i32s", list_i32(&[-1, 4, 3])), // min: -1, max: 4, sum: 6, avg: 2.0, len: 3 - ("p_i64s", list_i64(&[0, 3, -3])), // min: -3, max: 3, sum: 0, avg: 0.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.5, 3.0])), // min: 1.0, max: 3.0, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_f64s", list_f64(&[30.0, 60.0])), // min: 30.0, max: 60.0, sum: 90.0, avg: 45.0, len: 2 - ( - "nested_list", - list(vec![ - list(vec![ - list(vec![ - list(vec![50.0.into_prop(), 40.0.into_prop()]), - list(vec![60.0.into_prop()]), - ]), - list(vec![list(vec![46.0.into_prop()])]), - ]), - list(vec![list(vec![list(vec![90.0.into_prop()])])]), - ]), - ), - ], - ), - ( - 2, - "n3", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i64s", list_i64(&[1, 2, -3])), // min: -3, max: 2, sum: 0, avg: 0.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 - ( - "nested_list", - list(vec![ - list(vec![ - list(vec![ - list(vec![50.0.into_prop(), 40.0.into_prop()]), - list(vec![60.0.into_prop()]), - ]), - list(vec![list(vec![46.0.into_prop()])]), - ]), - list(vec![list(vec![list(vec![90.0.into_prop()])])]), - ]), - ), - ], - ), - ( - 1, - "n4", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_bools_all", list_bool(&[true, true])), - ], - ), - ( - 2, - "n4", - vec![ - ("p_strs", list_str(&["x", "y", "z"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[false, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u64s", list_u64(&[10, 20, 30])), // min: 10, max: 30, sum: 60, avg: 20.0, len: 3 - ("p_i32s", list_i32(&[10, 20, 30])), // min: 10, max: 30, sum: 60, avg: 20.0, len: 3 - ("p_f32s", list_f32(&[10.0, 20.0, 30.0])), // min: 10.0, max: 30.0, sum: 60.0, avg: 20.0, len: 3 - ("p_bools_all", list_bool(&[true, true])), - ], - ), - ( - 2, - "n5", - vec![ - ("p_u64s", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 - ("p_u64s_max", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 - ("p_u64s_min", list_u64(&[u64::MIN, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 - ("p_i64s", list_i64(&[i64::MAX, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 - ("p_i64s_max", list_i64(&[i64::MAX, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 - ("p_i64s_min", list_i64(&[i64::MIN, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 - ], - ), - ( - 2, - "n6", - vec![ - ("p_i32s", list_i32(&[-2, 1, 3])), // min: -2, max: 3, sum: 2, avg: 0.6666666666666666, len: 3 - ], - ), - ( - 1, - "n7", - vec![ - ("p_u64s", list_u64(&[])), // min: None, max: None, sum: None, avg: None, len: 0 - ], - ), - ( - 2, - "n10", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i64s", list_i64(&[1, 2, -3])), // min: -3, max: 2, sum: 0, avg: 0.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 - ("p_bools_all", list_bool(&[true, true])), - ], - ), - ]; + #[test] + fn test_node_property_metadata_len_f64s() { + let filter = NodeFilter.metadata("p_f64s").len().eq(Prop::U64(2)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - for (t, id, props) in nodes { - graph.add_node(t, id, props, None, None).unwrap(); + #[test] + fn test_node_property_metadata_len_strs() { + let filter = NodeFilter.metadata("p_strs").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n5"]; + apply_assertion(filter, &expected); } - let metadata: [(&str, Vec<(&str, Prop)>); 8] = [ - ( - "n1", - vec![ - ("p_u8s", list_u8(&[2, 9])), // min: 2, max: 9, sum: 11, avg: 5.5, len: 2 - ("p_u16s", list_u16(&[3, 5])), // min: 3, max: 5, sum: 8, avg: 4.0, len: 2 - ("p_u32s", list_u32(&[4, 9])), // min: 4, max: 9, sum: 13, avg: 6.5, len: 2 - ], - ), - ( - "n2", - vec![ - ("p_u64s", list_u64(&[2, 3, 7])), // min: 2, max: 7, sum: 12, avg: 4.0, len: 3 - ], - ), - ( - "n3", - vec![ - ("p_i32s", list_i32(&[10, 2, -3])), // min: -3, max: 10, sum: 9, avg: 3.0, len: 3 - ("p_i64s", list_i64(&[1, 12, 3, 4])), // min: 1, max: 12, sum: 20, avg: 5.0, len: 4 - ], - ), - ( - "n4", - vec![ - ("p_f32s", list_f32(&[1.5, 2.5])), // min: 1.5, max: 2.5, sum: 4.0, avg: 2.0, len: 2 - ("p_f64s", list_f64(&[0.5, 1.5])), // min: 0.5, max: 1.5, sum: 2.0, avg: 1.0, len: 2 - ], - ), - ( - "n5", - vec![ - ("p_strs", list_str(&["m1", "m2", "m3"])), // min: None, max: None, sum: None, avg: None, len: 3 - ], - ), - ( - "n6", - vec![ - ("p_u64s", list_u64(&[])), // min: None, max: None, sum: None, avg: None, len: 0 - ("p_strs", list_str(&["a", "a"])), - ], - ), - ( - "n7", - vec![ - ("p_u64s", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: ~9.22e18, len: 2 - ("p_strs", list_str(&["a"])), - ], - ), - ( - "n10", - vec![ - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ], - ), - ]; + // ------ Temporal last: SUM ------ + #[test] + fn test_node_property_temporal_last_sum_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - for (node_id, md) in metadata { - graph.node(node_id).unwrap().add_metadata(md).unwrap(); + #[test] + fn test_node_property_temporal_last_sum_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); } - graph - } + #[test] + fn test_node_property_temporal_last_sum_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - fn apply_assertion( - filter: impl TryAsCompositeFilter + CreateFilter + Clone, - expected: &[&str], - ) { - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected, - All, - ); + #[test] + fn test_node_property_temporal_last_sum_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .sum() + .eq(Prop::U64(60)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected, - All, - ); - } + #[test] + fn test_node_property_temporal_last_sum_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .sum() + .eq(Prop::I64(60)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - fn apply_assertion_err( - filter: impl TryAsCompositeFilter + CreateFilter + Clone, - expected: &str, - ) { - assert_filter_nodes_err( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected, - All, - ); - - // assert_search_nodes_err( - // init_nodes_graph, - // IdentityGraphTransformer, - // filter, - // expected, - // All, - // ); - } + #[test] + fn test_node_property_temporal_last_sum_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .sum() + .eq(Prop::I64(0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - // ------ Property: SUM ---- - #[test] - fn test_node_property_sum_u8s() { - let filter = NodeFilter.property("p_u8s").sum().eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_sum_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .sum() + .eq(Prop::F64(6.5)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_sum_u16s() { - let filter = NodeFilter.property("p_u16s").sum().eq(Prop::U64(6)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_sum_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .sum() + .eq(Prop::F64(90.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_sum_u32s() { - let filter = NodeFilter.property("p_u32s").sum().eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal last: AVG ------ + #[test] + fn test_node_property_temporal_last_avg_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_sum_u64s() { - let filter = NodeFilter.property("p_u64s").sum().eq(Prop::U64(6)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_avg_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_sum_i32s() { - let filter = NodeFilter.property("p_i32s").sum().eq(Prop::I64(2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_avg_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_sum_i64s() { - let filter = NodeFilter.property("p_i64s").sum().eq(Prop::I64(0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_avg_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .avg() + .eq(Prop::F64(20.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_sum_f32s() { - let filter = NodeFilter.property("p_f32s").sum().eq(Prop::F64(6.5)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .avg() + .eq(Prop::F64(0.6666666666666666)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_sum_f64s() { - let filter = NodeFilter.property("p_f64s").sum().eq(Prop::F64(120.0)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_avg_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .avg() + .eq(Prop::F64(0.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - // ------ Property: AVG ---- - #[test] - fn test_node_property_avg_u8s() { - let filter = NodeFilter.property("p_u8s").avg().eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .avg() + .eq(Prop::F64(20.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_avg_u16s() { - let filter = NodeFilter.property("p_u16s").avg().eq(Prop::F64(2.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_avg_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .avg() + .eq(Prop::F64(45.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_avg_u32s() { - let filter = NodeFilter.property("p_u32s").avg().eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal last: MIN ------ + #[test] + fn test_node_property_temporal_last_min_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .min() + .eq(Prop::U8(1)); + let expected = vec!["n1", "n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_avg_u64s() { - let filter = NodeFilter.property("p_u64s").avg().eq(Prop::F64(2.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_min_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .min() + .eq(Prop::U16(1)); + let expected = vec!["n1", "n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .avg() - .eq(Prop::F64(0.6666666666666666)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_min_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .min() + .eq(Prop::U32(1)); + let expected = vec!["n1", "n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_avg_i64s() { - let filter = NodeFilter.property("p_i64s").avg().eq(Prop::F64(0.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_min_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .min() + .eq(Prop::U64(10)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .avg() - .eq(Prop::F64(2.1666666666666665)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_min_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .min() + .eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_avg_f64s() { - let filter = NodeFilter.property("p_f64s").avg().eq(Prop::F64(40.0)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_min_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .min() + .eq(Prop::I64(-3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - // ------ Property: LEN ------ - #[test] - fn test_node_property_len_u8s() { - let filter = NodeFilter.property("p_u8s").len().eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_min_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .min() + .eq(Prop::F32(10.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_len_u16s() { - let filter = NodeFilter.property("p_u16s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_min_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .min() + .eq(Prop::F64(40.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_len_u32s() { - let filter = NodeFilter.property("p_u32s").len().eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal last: MAX ------ + #[test] + fn test_node_property_temporal_last_max_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .max() + .eq(Prop::U8(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_len_u64s() { - let filter = NodeFilter.property("p_u64s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_max_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .max() + .eq(Prop::U16(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_len_i32s() { - let filter = NodeFilter.property("p_i32s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_max_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .max() + .eq(Prop::U32(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_len_i64s() { - let filter = NodeFilter.property("p_i64s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_max_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .max() + .eq(Prop::U64(30)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_len_f32s() { - let filter = NodeFilter.property("p_f32s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_max_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .max() + .eq(Prop::I32(3)); + let expected = vec!["n3", "n6", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_len_f64s() { - let filter = NodeFilter.property("p_f64s").len().eq(Prop::U64(3)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_max_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_len_strs() { - let filter = NodeFilter.property("p_strs").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_max_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .max() + .eq(Prop::F32(3.5)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - // ------ Property: MIN ------ - #[test] - fn test_node_property_min_u8s() { - let filter = NodeFilter.property("p_u8s").min().eq(Prop::U8(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_max_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .max() + .eq(Prop::F64(50.0)); + let expected = vec!["n1", "n2", "n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_min_u16s() { - let filter = NodeFilter.property("p_u16s").min().eq(Prop::U16(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ Temporal last: LEN ------ + #[test] + fn test_node_property_temporal_last_len_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_min_u32s() { - let filter = NodeFilter.property("p_u32s").min().eq(Prop::U32(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_len_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_min_u64s() { - let filter = NodeFilter.property("p_u64s").min().eq(Prop::U64(1)); - let expected = vec!["n1", "n10", "n2", "n3", "n5"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_len_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_min_i32s() { - let filter = NodeFilter.property("p_i32s").min().eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_len_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n4", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_min_i64s() { - let filter = NodeFilter.property("p_i64s").min().eq(Prop::I64(-3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_len_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n4", "n6", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_min_f32s() { - let filter = NodeFilter.property("p_f32s").min().eq(Prop::F32(10.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_len_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_min_f64s() { - let filter = NodeFilter.property("p_f64s").min().eq(Prop::F64(40.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_len_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n4", "n10"]; + apply_assertion(filter, &expected); + } - // ------ Property: MAX ------ - #[test] - fn test_node_property_max_u8s() { - let filter = NodeFilter.property("p_u8s").max().eq(Prop::U8(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_last_len_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_max_u16s() { - let filter = NodeFilter.property("p_u16s").max().eq(Prop::U16(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ Temporal all: SUM ------ + #[test] + fn test_node_property_temporal_all_sum_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_max_u32s() { - let filter = NodeFilter.property("p_u32s").max().eq(Prop::U32(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_sum_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_max_u64s() { - let filter = NodeFilter.property("p_u64s").max().eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_sum_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_max_i32s() { - let filter = NodeFilter.property("p_i32s").max().eq(Prop::I32(3)); - let expected = vec!["n10", "n3", "n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_sum_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_max_i64s() { - let filter = NodeFilter.property("p_i64s").max().eq(Prop::I64(2)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_sum_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .sum() + .eq(Prop::I64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_max_f32s() { - let filter = NodeFilter.property("p_f32s").max().eq(Prop::F32(30.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_sum_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .sum() + .eq(Prop::I64(0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_max_f64s() { - let filter = NodeFilter.property("p_f64s").max().eq(Prop::F64(50.0)); - let expected = vec!["n1", "n10", "n2", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_sum_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .sum() + .eq(Prop::F64(6.5)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - // ------ Metadata: SUM ------ - #[test] - fn test_node_property_metadata_sum_u8s() { - let filter = NodeFilter.metadata("p_u8s").sum().eq(Prop::U64(11)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_sum_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .sum() + .eq(Prop::F64(90.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_sum_u16s() { - let filter = NodeFilter.metadata("p_u16s").sum().eq(Prop::U64(8)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal all: AVG ------ + #[test] + fn test_node_property_temporal_all_avg_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_sum_u32s() { - let filter = NodeFilter.metadata("p_u32s").sum().eq(Prop::U64(13)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_avg_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_sum_u64s() { - let filter = NodeFilter.metadata("p_u64s").sum().eq(Prop::U64(12)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_avg_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_sum_i32s() { - let filter = NodeFilter.metadata("p_i32s").sum().eq(Prop::I64(9)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_avg_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_sum_i64s() { - let filter = NodeFilter.metadata("p_i64s").sum().eq(Prop::I64(20)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_sum_f32s() { - let filter = NodeFilter.metadata("p_f32s").sum().eq(Prop::F64(4.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_avg_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .avg() + .eq(Prop::F64(0.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_sum_f64s() { - let filter = NodeFilter.metadata("p_f64s").sum().eq(Prop::F64(2.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.1666666666666665)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - // ------ Metadata: AVG ------ - #[test] - fn test_node_property_metadata_avg_u8s() { - let filter = NodeFilter.metadata("p_u8s").avg().eq(Prop::F64(5.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_avg_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .avg() + .eq(Prop::F64(45.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_avg_u16s() { - let filter = NodeFilter.metadata("p_u16s").avg().eq(Prop::F64(4.0)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal all: MIN ------ + #[test] + fn test_node_property_temporal_all_min_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .min() + .eq(Prop::U8(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_avg_u32s() { - let filter = NodeFilter.metadata("p_u32s").avg().eq(Prop::F64(6.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_min_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .min() + .eq(Prop::U16(1)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_avg_u64s() { - let filter = NodeFilter.metadata("p_u64s").avg().eq(Prop::F64(4.0)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_min_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .min() + .eq(Prop::U32(1)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_avg_i32s() { - let filter = NodeFilter.metadata("p_i32s").avg().eq(Prop::F64(3.0)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_min_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .min() + .eq(Prop::U64(1)); + let expected = vec!["n1", "n10", "n2", "n5"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_avg_i64s() { - let filter = NodeFilter.metadata("p_i64s").avg().eq(Prop::F64(5.0)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_min_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .min() + .eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_avg_f32s() { - let filter = NodeFilter.metadata("p_f32s").avg().eq(Prop::F64(2.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_min_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .min() + .eq(Prop::I64(-3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_avg_f64s() { - let filter = NodeFilter.metadata("p_f64s").avg().eq(Prop::F64(1.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_min_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .min() + .eq(Prop::F32(1.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - // ------ Metadata: MIN ------ - #[test] - fn test_node_property_metadata_min_u8s() { - let filter = NodeFilter.metadata("p_u8s").min().eq(Prop::U8(2)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_min_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .min() + .eq(Prop::F64(30.0)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_min_u16s() { - let filter = NodeFilter.metadata("p_u16s").min().eq(Prop::U16(3)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal all: MAX ------ + #[test] + fn test_node_property_temporal_all_max_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .max() + .eq(Prop::U8(3)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_min_u32s() { - let filter = NodeFilter.metadata("p_u32s").min().eq(Prop::U32(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_max_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .max() + .eq(Prop::U16(3)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_min_u64s() { - let filter = NodeFilter.metadata("p_u64s").min().eq(Prop::U64(2)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_max_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .max() + .eq(Prop::U32(3)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_min_i32s() { - let filter = NodeFilter.metadata("p_i32s").min().eq(Prop::I32(-3)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_max_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .max() + .eq(Prop::U64(4)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_min_i64s() { - let filter = NodeFilter.metadata("p_i64s").min().eq(Prop::I64(1)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_max_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .max() + .eq(Prop::I32(3)); + let expected = vec!["n10", "n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_min_f32s() { - let filter = NodeFilter.metadata("p_f32s").min().eq(Prop::F32(1.5)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_max_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_min_f64s() { - let filter = NodeFilter.metadata("p_f64s").min().eq(Prop::F64(0.5)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_max_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .max() + .eq(Prop::F32(3.5)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } - // ------ Metadata: MAX ------ - #[test] - fn test_node_property_metadata_max_u8s() { - let filter = NodeFilter.metadata("p_u8s").max().eq(Prop::U8(9)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_max_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .max() + .eq(Prop::F64(50.0)); + let expected = vec!["n1", "n10", "n2"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_max_u16s() { - let filter = NodeFilter.metadata("p_u16s").max().eq(Prop::U16(5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal all: LEN ------ + #[test] + fn test_node_property_temporal_all_len_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_max_u32s() { - let filter = NodeFilter.metadata("p_u32s").max().eq(Prop::U32(9)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_len_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_max_u64s() { - let filter = NodeFilter.metadata("p_u64s").max().eq(Prop::U64(7)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_len_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_max_i32s() { - let filter = NodeFilter.metadata("p_i32s").max().eq(Prop::I32(10)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_len_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_max_i64s() { - let filter = NodeFilter.metadata("p_i64s").max().eq(Prop::I64(12)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_len_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_max_f32s() { - let filter = NodeFilter.metadata("p_f32s").max().eq(Prop::F32(2.5)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_len_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_max_f64s() { - let filter = NodeFilter.metadata("p_f64s").max().eq(Prop::F64(1.5)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_len_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - // ------ Metadata: Len ------ - #[test] - fn test_node_property_metadata_len_u8s() { - let filter = NodeFilter.metadata("p_u8s").len().eq(Prop::U64(2)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_all_len_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_len_u16s() { - let filter = NodeFilter.metadata("p_u16s").len().eq(Prop::U64(2)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal first: SUM ------ + #[test] + fn test_node_property_temporal_first_sum_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_len_u32s() { - let filter = NodeFilter.metadata("p_u32s").len().eq(Prop::U64(2)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_sum_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_len_u64s() { - let filter = NodeFilter.metadata("p_u64s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_sum_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_len_i32s() { - let filter = NodeFilter.metadata("p_i32s").len().eq(Prop::U64(3)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_sum_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_len_i64s() { - let filter = NodeFilter.metadata("p_i64s").len().eq(Prop::U64(4)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_sum_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .sum() + .eq(Prop::I64(6)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_len_f32s() { - let filter = NodeFilter.metadata("p_f32s").len().eq(Prop::U64(2)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_sum_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .sum() + .eq(Prop::I64(0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_len_f64s() { - let filter = NodeFilter.metadata("p_f64s").len().eq(Prop::U64(2)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_sum_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .sum() + .eq(Prop::F64(6.5)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_metadata_len_strs() { - let filter = NodeFilter.metadata("p_strs").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n5"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_sum_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .sum() + .eq(Prop::F64(90.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - // ------ Temporal last: SUM ------ - #[test] - fn test_node_property_temporal_last_sum_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal first: AVG ------ + #[test] + fn test_node_property_temporal_first_avg_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_sum_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_avg_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_sum_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_avg_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_sum_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .sum() - .eq(Prop::U64(60)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_avg_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_sum_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .sum() - .eq(Prop::I64(60)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_sum_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .sum() - .eq(Prop::I64(0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_avg_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .avg() + .eq(Prop::F64(0.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_sum_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .sum() - .eq(Prop::F64(6.5)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.1666666666666665)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_sum_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .sum() - .eq(Prop::F64(90.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_avg_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .avg() + .eq(Prop::F64(45.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - // ------ Temporal last: AVG ------ - #[test] - fn test_node_property_temporal_last_avg_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal first: MIN ------ + #[test] + fn test_node_property_temporal_first_min_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .min() + .eq(Prop::U8(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_avg_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_min_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .min() + .eq(Prop::U16(1)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_avg_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_min_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .min() + .eq(Prop::U32(1)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_avg_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .avg() - .eq(Prop::F64(20.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_min_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .min() + .eq(Prop::U64(1)); + let expected = vec!["n1", "n10", "n2", "n4", "n5"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .avg() - .eq(Prop::F64(0.6666666666666666)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_min_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .min() + .eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_avg_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .avg() - .eq(Prop::F64(0.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_min_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .min() + .eq(Prop::I64(-3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .avg() - .eq(Prop::F64(20.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_min_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .min() + .eq(Prop::F32(1.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_avg_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .avg() - .eq(Prop::F64(45.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_min_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .min() + .eq(Prop::F64(30.0)); + let expected = vec!["n2", "n3"]; + apply_assertion(filter, &expected); + } - // ------ Temporal last: MIN ------ - #[test] - fn test_node_property_temporal_last_min_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .min() - .eq(Prop::U8(1)); - let expected = vec!["n1", "n3", "n10"]; - apply_assertion(filter, &expected); - } + // ------ Temporal first: MAX ------ + #[test] + fn test_node_property_temporal_first_max_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .max() + .eq(Prop::U8(3)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_min_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .min() - .eq(Prop::U16(1)); - let expected = vec!["n1", "n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_max_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .max() + .eq(Prop::U16(3)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_min_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .min() - .eq(Prop::U32(1)); - let expected = vec!["n1", "n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_max_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .max() + .eq(Prop::U32(3)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_min_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .min() - .eq(Prop::U64(10)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_max_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .max() + .eq(Prop::U64(4)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_min_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .min() - .eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_max_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .max() + .eq(Prop::I32(3)); + let expected = vec!["n1", "n10", "n4", "n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_min_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .min() - .eq(Prop::I64(-3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_max_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_min_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .min() - .eq(Prop::F32(10.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_max_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .max() + .eq(Prop::F32(3.5)); + let expected = vec!["n1", "n10", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_min_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .min() - .eq(Prop::F64(40.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_max_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .max() + .eq(Prop::F64(50.0)); + let expected = vec!["n1", "n10", "n2"]; + apply_assertion(filter, &expected); + } - // ------ Temporal last: MAX ------ - #[test] - fn test_node_property_temporal_last_max_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .max() - .eq(Prop::U8(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // ------ Temporal first: LEN ------ + #[test] + fn test_node_property_temporal_first_len_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_max_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .max() - .eq(Prop::U16(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_len_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_max_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .max() - .eq(Prop::U32(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_len_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_max_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .max() - .eq(Prop::U64(30)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_len_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_max_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .max() - .eq(Prop::I32(3)); - let expected = vec!["n3", "n6", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_len_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_max_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_len_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_max_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .max() - .eq(Prop::F32(3.5)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_len_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_max_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .max() - .eq(Prop::F64(50.0)); - let expected = vec!["n1", "n2", "n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_first_len_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - // ------ Temporal last: LEN ------ - #[test] - fn test_node_property_temporal_last_len_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + // ------ Temporal any: SUM ------ + #[test] + fn test_node_property_temporal_any_sum_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_len_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_sum_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_len_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_sum_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_len_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n4", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_sum_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_len_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n4", "n6", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_sum_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .sum() + .eq(Prop::I64(6)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .sum() + .eq(Prop::I64(60)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_len_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_sum_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .sum() + .eq(Prop::I64(0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .sum() + .eq(Prop::I64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_len_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n4", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_sum_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .sum() + .eq(Prop::F64(6.5)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .sum() + .eq(Prop::F64(60.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_last_len_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_sum_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .sum() + .eq(Prop::F64(90.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .sum() + .eq(Prop::F64(120.0)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } - // ------ Temporal all: SUM ------ - #[test] - fn test_node_property_temporal_all_sum_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + // ------ Temporal any: AVG ------ + #[test] + fn test_node_property_temporal_any_avg_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_sum_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_avg_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_sum_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_avg_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_sum_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_avg_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_sum_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .sum() - .eq(Prop::I64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_sum_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .sum() - .eq(Prop::I64(0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_avg_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(0.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_sum_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .sum() - .eq(Prop::F64(6.5)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.1666666666666665)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(20.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_sum_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .sum() - .eq(Prop::F64(90.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_avg_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(45.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(40.0)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } - // ------ Temporal all: AVG ------ - #[test] - fn test_node_property_temporal_all_avg_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + // ------ Temporal any: MIN ------ + #[test] + fn test_node_property_temporal_any_min_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .min() + .eq(Prop::U8(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_avg_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_min_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .min() + .eq(Prop::U16(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_avg_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_min_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .min() + .eq(Prop::U32(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_avg_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_min_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .min() + .eq(Prop::U64(1)); + let expected = vec!["n1", "n10", "n2", "n3", "n4", "n5"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_min_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .min() + .eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .min() + .eq(Prop::I32(10)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_avg_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .avg() - .eq(Prop::F64(0.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_min_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .min() + .eq(Prop::I64(-3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .min() + .eq(Prop::I64(1)); + let expected = vec!["n1", "n5"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.1666666666666665)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_min_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .min() + .eq(Prop::F32(1.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .min() + .eq(Prop::F32(10.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_avg_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .avg() - .eq(Prop::F64(45.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_min_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .min() + .eq(Prop::F64(30.0)); + let expected = vec!["n1", "n2", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .min() + .eq(Prop::F64(40.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - // ------ Temporal all: MIN ------ - #[test] - fn test_node_property_temporal_all_min_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .min() - .eq(Prop::U8(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ Temporal any: MAX ------ + #[test] + fn test_node_property_temporal_any_max_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .max() + .eq(Prop::U8(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .max() + .eq(Prop::U8(4)); + let expected = vec!["n1", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_min_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .min() - .eq(Prop::U16(1)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_max_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .max() + .eq(Prop::U16(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .max() + .eq(Prop::U16(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_min_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .min() - .eq(Prop::U32(1)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_max_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .max() + .eq(Prop::U32(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .max() + .eq(Prop::U32(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_min_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .min() - .eq(Prop::U64(1)); - let expected = vec!["n1", "n10", "n2", "n5"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_max_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .max() + .eq(Prop::U64(4)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .max() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_min_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .min() - .eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_max_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .max() + .eq(Prop::I32(3)); + let expected = vec!["n1", "n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .max() + .eq(Prop::I32(30)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_min_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .min() - .eq(Prop::I64(-3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_max_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_min_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .min() - .eq(Prop::F32(1.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_max_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .max() + .eq(Prop::F32(3.5)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .max() + .eq(Prop::F32(30.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_min_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .min() - .eq(Prop::F64(30.0)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_max_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .max() + .eq(Prop::F64(50.0)); + let expected = vec!["n1", "n10", "n2", "n3"]; + apply_assertion(filter, &expected); + } - // ------ Temporal all: MAX ------ - #[test] - fn test_node_property_temporal_all_max_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .max() - .eq(Prop::U8(3)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } + // ------ Temporal any: LEN ------ + #[test] + fn test_node_property_temporal_any_len_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .len() + .is_in(vec![Prop::U64(3)]); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_max_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .max() - .eq(Prop::U16(3)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_len_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_max_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .max() - .eq(Prop::U32(3)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_len_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_max_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .max() - .eq(Prop::U64(4)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_len_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_max_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .max() - .eq(Prop::I32(3)); - let expected = vec!["n10", "n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_len_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_max_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_len_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_max_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .max() - .eq(Prop::F32(3.5)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_len_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_max_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .max() - .eq(Prop::F64(50.0)); - let expected = vec!["n1", "n10", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_property_temporal_any_len_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } - // ------ Temporal all: LEN ------ - #[test] - fn test_node_property_temporal_all_len_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ EMPTY LISTS ------ + #[test] + fn test_empty_list_agg() { + let filter = NodeFilter.property("p_u64s").sum().eq(Prop::U64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_u64s").avg().eq(Prop::F64(0.0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .min() + .eq(Prop::U64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .max() + .eq(Prop::U64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_u64s").len().eq(Prop::U64(0)); + let expected: Vec<&str> = vec!["n7"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.metadata("p_u64s").len().eq(Prop::U64(0)); + let expected: Vec<&str> = vec!["n6"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_len_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ Unsupported filter operations ------ + #[test] + fn test_unsupported_filter_ops_agg() { + let filter = NodeFilter.property("p_u64s").sum().starts_with("abc"); + let expected: &str = "Operator STARTS_WITH is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").avg().ends_with("abc"); + let expected: &str = "Operator ENDS_WITH is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").min().is_none(); + let expected: &str = "Operator IS_NONE is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").max().is_some(); + let expected: &str = "Operator IS_SOME is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").len().contains("abc"); + let expected: &str = "Operator CONTAINS is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").sum().not_contains("abc"); + let expected: &str = "Operator NOT_CONTAINS is not supported with list aggregation"; + apply_assertion_err(filter, expected); + } - #[test] - fn test_node_property_temporal_all_len_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + // --------------- OVERFLOW --------------- + #[test] + fn test_max_value_agg() { + let filter = NodeFilter + .property("p_u64s_max") + .max() + .eq(Prop::U64(u64::MAX)); + let expected: Vec<&str> = vec!["n5", "n1"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s_min") + .min() + .eq(Prop::U64(u64::MIN)); + let expected: Vec<&str> = vec!["n5"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_u8s_max").sum().eq(Prop::U64(510)); + let expected: Vec<&str> = vec!["n1"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s_max") + .sum() + .eq(Prop::U64(131070)); + let expected: Vec<&str> = vec!["n1"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s_max") + .sum() + .eq(Prop::U64(8589934590)); + let expected: Vec<&str> = vec!["n1"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_u64s_max").sum().gt(Prop::U64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + // AVG is computed in f64 even if SUM overflowed. + let avg = (u64::MAX as f64 + 1.0) / 2.0; + let filter = NodeFilter.property("p_u64s_max").avg().eq(avg); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_i64s_max").sum().gt(Prop::I64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + // AVG is computed in f64 even if SUM overflowed. + let avg = (i64::MAX as f64 + 1.0) / 2.0; + let filter = NodeFilter.property("p_i64s_max").avg().eq(avg); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_len_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } + // ------ Property: any ------ + #[test] + fn test_node_property_any() { + let filter = NodeFilter.property("p_u8s").any().eq(Prop::U8(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_len_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - } + // ------ Property: all ------ + #[test] + fn test_node_property_all() { + let filter = NodeFilter + .property("p_bools_all") + .all() + .eq(Prop::Bool(true)); + let expected = vec!["n10", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_len_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - } + // ------ Metadata: any ------ + #[test] + fn test_node_metadata_any() { + let filter = NodeFilter.metadata("p_u64s").any().eq(Prop::U64(1)); + let expected = vec!["n10", "n7"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_len_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + // ------ Metadata: all ------ + #[test] + fn test_node_metadata_all() { + let filter = NodeFilter.metadata("p_strs").all().eq("a"); + let expected = vec!["n6", "n7"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_all_len_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ Temporal First: any ------ + #[test] + fn test_node_temporal_property_first_any() { + let filter = NodeFilter + .property("p_bools") + .temporal() + .first() + .any() + .eq(false); + let expected = vec!["n1", "n10", "n2", "n3", "n4"]; + apply_assertion(filter, &expected); + } - // ------ Temporal first: SUM ------ - #[test] - fn test_node_property_temporal_first_sum_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ Temporal First: all ------ + #[test] + fn test_node_temporal_property_first_all() { + let filter = NodeFilter + .property("p_bools_all") + .temporal() + .first() + .all() + .eq(true); + let expected = vec!["n10", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_first_sum_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ Temporal last: any ------ + #[test] + fn test_node_temporal_property_last_any() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .any() + .eq(Prop::F32(3.5)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_first_sum_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ Temporal last: all ------ + #[test] + fn test_node_temporal_property_last_all() { + let filter = NodeFilter + .property("p_bools_all") + .temporal() + .last() + .all() + .eq(true); + let expected = vec!["n10", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_first_sum_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + // ------ Temporal Any: any ------ + #[test] + fn test_node_temporal_property_any_any() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .any() + .eq(Prop::F32(3.5)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .any() + .eq(Prop::F32(30.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_first_sum_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .sum() - .eq(Prop::I64(6)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + // ------ Temporal Any: all ------ + #[test] + fn test_node_temporal_property_any_all() { + let filter = NodeFilter + .property("p_bools") + .temporal() + .any() + .all() + .eq(false); + let expected = vec!["n2", "n4"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_first_sum_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .sum() - .eq(Prop::I64(0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_nested_list_property_all_all_all_any() { + let filter = NodeFilter + .property("nested_list") + .all() + .all() + .all() + .any() + .gt(45.0); + + let expected = vec!["n1", "n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_first_sum_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .sum() - .eq(Prop::F64(6.5)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_node_nested_list_temporal_property_all_all_all_all_any() { + let filter = NodeFilter + .property("nested_list") + .temporal() + .all() + .all() + .all() + .all() + .any() + .gt(45.0); + + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } - #[test] - fn test_node_property_temporal_first_sum_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .sum() - .eq(Prop::F64(90.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + // ------ Temporal All: any ------ + #[test] + fn test_node_temporal_property_all_any() { + let filter = NodeFilter + .property("p_bools") + .temporal() + .all() + .any() + .eq(true); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } - // ------ Temporal first: AVG ------ - #[test] - fn test_node_property_temporal_first_avg_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); + // ------ Temporal All: all ------ + #[test] + fn test_node_temporal_property_all_all() { + let filter = NodeFilter + .property("p_bools_all") + .temporal() + .all() + .all() + .eq(true); + let expected = vec!["n4", "n10"]; + apply_assertion(filter, &expected); + } } - #[test] - fn test_node_property_temporal_first_avg_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[cfg(test)] + mod test_edge_filter { + use crate::test::{ + init_edges_graph, init_edges_graph_with_num_ids, init_edges_graph_with_str_ids, + init_edges_graph_with_str_ids_del, init_nodes_graph, IdentityGraphTransformer, + }; + use raphtory::db::graph::{ + assertions::{ + assert_filter_edges_results, assert_search_edges_results, + assert_select_edges_results, TestGraphVariants, TestVariants, + }, + views::filter::model::{ + edge_filter::EdgeFilter, + node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, + property_filter::ops::{ListAggOps, PropertyFilterOps}, + ComposableFilter, EdgeViewFilterOps, PropertyFilterFactory, + TemporalPropertyFilterFactory, ViewWrapOps, + }, + }; - #[test] - fn test_node_property_temporal_first_avg_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_src_property_eq() { + let filter = EdgeFilter::src().property("p10").eq("Paper_airplane"); + let expected_results = vec!["1->2", "3->1"]; + let g = |g| init_edges_graph(init_nodes_graph(g)); + assert_filter_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_avg_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_src_property_temporal_eq() { + let filter = EdgeFilter::src() + .property("p30") + .temporal() + .first() + .eq("Old_boat"); + let expected_results = vec!["2->1", "2->3"]; + let g = |g| init_edges_graph(init_nodes_graph(g)); + assert_filter_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_src_metadata_eq() { + let filter = EdgeFilter::src().metadata("m1").eq("pometry"); + let expected_results = vec!["1->2"]; + let g = |g| init_edges_graph(init_nodes_graph(g)); + assert_filter_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_avg_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .avg() - .eq(Prop::F64(0.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_eq() { + let filter = EdgeFilter::src().name().eq("3"); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.1666666666666665)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_ne() { + let filter = EdgeFilter::src().name().ne("1"); + let expected_results = vec![ + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_avg_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .avg() - .eq(Prop::F64(45.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_in() { + let filter = EdgeFilter::src().name().is_in(vec!["1"]); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Temporal first: MIN ------ - #[test] - fn test_node_property_temporal_first_min_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .min() - .eq(Prop::U8(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::src().name().is_in(vec!["1", "2"]); + let expected_results = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_min_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .min() - .eq(Prop::U16(1)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_not_in() { + let filter = EdgeFilter::src().name().is_not_in(vec!["1"]); + let expected_results = vec![ + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_min_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .min() - .eq(Prop::U32(1)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_eq() { + let filter = EdgeFilter::dst().name().eq("2"); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_min_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .min() - .eq(Prop::U64(1)); - let expected = vec!["n1", "n10", "n2", "n4", "n5"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_ne() { + let filter = EdgeFilter::dst().name().ne("2"); + let expected_results = vec![ + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_min_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .min() - .eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_in() { + let filter = EdgeFilter::dst().name().is_in(vec!["2"]); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_first_min_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .min() - .eq(Prop::I64(-3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::dst().name().is_in(vec!["2", "3"]); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_min_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .min() - .eq(Prop::F32(1.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_not_in() { + let filter = EdgeFilter::dst().name().is_not_in(vec!["1"]); + let expected_results = vec![ + "1->2", + "2->3", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_min_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .min() - .eq(Prop::F64(30.0)); - let expected = vec!["n2", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_dst_starts_with() { + let filter = EdgeFilter::src().name().starts_with("Joh"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Temporal first: MAX ------ - #[test] - fn test_node_property_temporal_first_max_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .max() - .eq(Prop::U8(3)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::src().name().starts_with("Joker"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_first_max_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .max() - .eq(Prop::U16(3)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::dst().name().starts_with("Jimmy"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_first_max_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .max() - .eq(Prop::U32(3)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::dst().name().starts_with("Tango"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_max_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .max() - .eq(Prop::U64(4)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_dst_ends_with() { + let filter = EdgeFilter::src().name().ends_with("Mayer"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_first_max_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .max() - .eq(Prop::I32(3)); - let expected = vec!["n1", "n10", "n4", "n6"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::src().name().ends_with("Cruise"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_first_max_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::dst().name().ends_with("Page"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_first_max_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .max() - .eq(Prop::F32(3.5)); - let expected = vec!["n1", "n10", "n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::dst().name().ends_with("Cruise"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_max_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .max() - .eq(Prop::F64(50.0)); - let expected = vec!["n1", "n10", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_contains() { + let filter = EdgeFilter::src().name().contains("Mayer"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - // ------ Temporal first: LEN ------ - #[test] - fn test_node_property_temporal_first_len_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_contains_not() { + let filter = EdgeFilter::src().name().not_contains("Mayer"); + let expected_results: Vec<&str> = + vec!["1->2", "2->1", "2->3", "3->1", "David Gilmour->John Mayer"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_len_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_fuzzy_search() { + let filter = EdgeFilter::src().name().fuzzy_search("John", 2, true); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_first_len_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::src().name().fuzzy_search("John", 2, false); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_first_len_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::src().name().fuzzy_search("John May", 2, false); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_len_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_not_src() { + let filter = EdgeFilter::src().name().is_not_in(vec!["1"]).not(); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_len_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_id_eq() { + let filter = EdgeFilter::src().id().eq("3"); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_first_len_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::src().id().eq(3); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_first_len_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_id_eq() { + let filter = EdgeFilter::dst().id().eq("3"); + let expected_results = vec!["2->3"]; + // assert_filter_edges_results( + // init_edges_graph, + // IdentityGraphTransformer, + // filter.clone(), + // &expected_results, + // TestVariants::All, + // ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Temporal any: SUM ------ - #[test] - fn test_node_property_temporal_any_sum_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::dst().id().eq(3); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_sum_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_id_ne() { + let filter = EdgeFilter::src().id().ne("3"); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_any_sum_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::src().id().ne(3); + let expected_results = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_sum_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_id_ne() { + let filter = EdgeFilter::dst().id().ne("3"); + let expected_results = vec![ + "1->2", + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_any_sum_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .sum() - .eq(Prop::I64(6)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .sum() - .eq(Prop::I64(60)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::dst().id().ne(3); + let expected_results = vec!["1->2", "2->1", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_sum_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .sum() - .eq(Prop::I64(0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .sum() - .eq(Prop::I64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_id_src_is_in() { + let filter = EdgeFilter::src().id().is_in(vec!["3"]); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_any_sum_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .sum() - .eq(Prop::F64(6.5)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .sum() - .eq(Prop::F64(60.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::src().id().is_in(vec![3]); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_sum_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .sum() - .eq(Prop::F64(90.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .sum() - .eq(Prop::F64(120.0)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_id_dst_is_in() { + let filter = EdgeFilter::dst().id().is_in(vec!["3"]); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Temporal any: AVG ------ - #[test] - fn test_node_property_temporal_any_avg_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::dst().id().is_in(vec![3]); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_avg_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_id_src_is_not_in() { + let filter = EdgeFilter::src().id().is_not_in(vec!["3"]); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_any_avg_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::src().id().is_not_in(vec![3]); + let expected_results = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_avg_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_id_dst_is_not_in() { + let filter = EdgeFilter::dst().id().is_not_in(vec!["3"]); + let expected_results = vec![ + "1->2", + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_property_temporal_any_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter::dst().id().is_not_in(vec![3]); + let expected_results = vec!["1->2", "2->1", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_avg_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(0.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_id_lt() { + let filter = EdgeFilter::src().id().lt(3); + let expected_results = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.1666666666666665)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(20.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_id_lt() { + let filter = EdgeFilter::dst().id().lt(3); + let expected_results = vec!["1->2", "2->1", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_avg_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(45.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(40.0)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_id_le() { + let filter = EdgeFilter::src().id().le(3); + let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - // ------ Temporal any: MIN ------ - #[test] - fn test_node_property_temporal_any_min_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .min() - .eq(Prop::U8(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_id_le() { + let filter = EdgeFilter::dst().id().le(3); + let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_min_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .min() - .eq(Prop::U16(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_id_gt() { + let filter = EdgeFilter::src().id().gt(1); + let expected_results = vec!["2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_min_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .min() - .eq(Prop::U32(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_id_gt() { + let filter = EdgeFilter::dst().id().gt(1); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_min_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .min() - .eq(Prop::U64(1)); - let expected = vec!["n1", "n10", "n2", "n3", "n4", "n5"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_id_ge() { + let filter = EdgeFilter::src().id().ge(1); + let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_min_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .min() - .eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .min() - .eq(Prop::I32(10)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_id_ge() { + let filter = EdgeFilter::dst().id().ge(1); + let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_min_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .min() - .eq(Prop::I64(-3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .min() - .eq(Prop::I64(1)); - let expected = vec!["n1", "n5"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_name_starts_with() { + let filter = EdgeFilter::src().name().starts_with("Tw"); + let expected_results = vec!["Two->One", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_min_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .min() - .eq(Prop::F32(1.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .min() - .eq(Prop::F32(10.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_id_ends_with() { + let filter = EdgeFilter::src().id().ends_with("don"); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_min_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .min() - .eq(Prop::F64(30.0)); - let expected = vec!["n1", "n2", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .min() - .eq(Prop::F64(40.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_id_contains() { + let filter = EdgeFilter::src().id().contains("don"); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - // ------ Temporal any: MAX ------ - #[test] - fn test_node_property_temporal_any_max_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .max() - .eq(Prop::U8(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .max() - .eq(Prop::U8(4)); - let expected = vec!["n1", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_id_contains() { + let filter = EdgeFilter::dst().id().contains("Par"); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_max_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .max() - .eq(Prop::U16(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .max() - .eq(Prop::U16(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_name_not_contains() { + let filter = EdgeFilter::dst().name().not_contains("Par"); + let expected_results = vec![ + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + "Three->One", + "Two->One", + "Two->Three", + ]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_max_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .max() - .eq(Prop::U32(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .max() - .eq(Prop::U32(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_src_id_is_in() { + let filter = EdgeFilter::src().id().is_in(["Two"]); + let expected_results = vec!["Two->One", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_max_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .max() - .eq(Prop::U64(4)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .max() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_dst_id_is_not_in() { + let filter = EdgeFilter::dst().id().is_not_in(["One"]); + let expected_results = vec![ + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + "London->Paris", + "Two->Three", + ]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_max_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .max() - .eq(Prop::I32(3)); - let expected = vec!["n1", "n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .max() - .eq(Prop::I32(30)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_is_active_edge_window() { + let filter = EdgeFilter.window(1, 3).is_active(); + let expected_results = vec!["London->Paris", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_max_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_is_active_edge_after() { + let filter = EdgeFilter.after(3).is_active(); + let expected_results = vec![ + "Bangalore->Bangalore", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_max_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .max() - .eq(Prop::F32(3.5)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .max() - .eq(Prop::F32(30.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_is_active_edge_before() { + let filter = EdgeFilter.before(3).is_active(); + let expected_results = vec!["London->Paris", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_max_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .max() - .eq(Prop::F64(50.0)); - let expected = vec!["n1", "n10", "n2", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_is_active_edge_snapshot_latest() { + let filter = EdgeFilter.snapshot_latest().is_active(); + let expected_results = vec!["Bangalore->Bangalore"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } - // ------ Temporal any: LEN ------ - #[test] - fn test_node_property_temporal_any_len_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .len() - .is_in(vec![Prop::U64(3)]); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_is_valid_edge_window() { + let filter = EdgeFilter.window(1, 3).is_valid(); + let expected_results = vec!["London->Paris", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); - #[test] - fn test_node_property_temporal_any_len_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.window(1, 4).is_valid(); + let expected_results = vec!["Three->One", "Two->One", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + } - #[test] - fn test_node_property_temporal_any_len_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_is_valid_edge_snapshot_at() { + let filter = EdgeFilter.snapshot_at(2).is_valid(); + let expected_results = vec!["London->Paris", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); - #[test] - fn test_node_property_temporal_any_len_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.snapshot_at(3).is_valid(); + let expected_results = vec!["Three->One", "Two->One", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + } - #[test] - fn test_node_property_temporal_any_len_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_is_valid_edge_snapshot_latest() { + let filter = EdgeFilter.snapshot_latest().is_valid(); + let expected_results = vec![ + "Bangalore->Bangalore", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + "Three->One", + "Two->One", + "Two->Three", + ]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + } - #[test] - fn test_node_property_temporal_any_len_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // Disk graph doesn't support deletions + #[test] + fn test_is_deleted_edge_after() { + let filter = EdgeFilter.after(1).is_deleted(); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_len_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } + // Disk graph doesn't support deletions + #[test] + fn test_is_deleted_edge_before() { + let filter = EdgeFilter.before(4).is_deleted(); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_node_property_temporal_any_len_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_is_self_loop_edge_window() { + let filter = EdgeFilter.window(1, 3).is_self_loop(); + let expected_results = vec![]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ EMPTY LISTS ------ - #[test] - fn test_empty_list_agg() { - let filter = NodeFilter.property("p_u64s").sum().eq(Prop::U64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_u64s").avg().eq(Prop::F64(0.0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .min() - .eq(Prop::U64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .max() - .eq(Prop::U64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_u64s").len().eq(Prop::U64(0)); - let expected: Vec<&str> = vec!["n7"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.metadata("p_u64s").len().eq(Prop::U64(0)); - let expected: Vec<&str> = vec!["n6"]; - apply_assertion(filter, &expected); + let filter = EdgeFilter.window(1, 6).is_self_loop(); + let expected_results = vec!["Bangalore->Bangalore"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } } - // ------ Unsupported filter operations ------ - #[test] - fn test_unsupported_filter_ops_agg() { - let filter = NodeFilter.property("p_u64s").sum().starts_with("abc"); - let expected: &str = "Operator STARTS_WITH is not supported with list aggregation"; - apply_assertion_err(filter, expected); - - let filter = NodeFilter.property("p_u64s").avg().ends_with("abc"); - let expected: &str = "Operator ENDS_WITH is not supported with list aggregation"; - apply_assertion_err(filter, expected); - - let filter = NodeFilter.property("p_u64s").min().is_none(); - let expected: &str = "Operator IS_NONE is not supported with list aggregation"; - apply_assertion_err(filter, expected); - - let filter = NodeFilter.property("p_u64s").max().is_some(); - let expected: &str = "Operator IS_SOME is not supported with list aggregation"; - apply_assertion_err(filter, expected); + #[cfg(test)] + mod test_edge_property_filter { + use crate::test::{init_edges_graph, init_edges_graph2, IdentityGraphTransformer}; + use raphtory::db::graph::{ + assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, + TestVariants, + }, + views::filter::model::{ + edge_filter::EdgeFilter, + property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, + ComposableFilter, PropertyFilterFactory, TemporalPropertyFilterFactory, + ViewWrapOps, + }, + }; + use raphtory_api::core::entities::properties::prop::Prop; - let filter = NodeFilter.property("p_u64s").len().contains("abc"); - let expected: &str = "Operator CONTAINS is not supported with list aggregation"; - apply_assertion_err(filter, expected); + #[test] + fn test_filter_edges_for_property_eq() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").eq(2u64); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - let filter = NodeFilter.property("p_u64s").sum().not_contains("abc"); - let expected: &str = "Operator NOT_CONTAINS is not supported with list aggregation"; - apply_assertion_err(filter, expected); - } + let filter = EdgeFilter.property("p30").temporal().first().eq("Old_boat"); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // --------------- OVERFLOW --------------- - #[test] - fn test_max_value_agg() { - let filter = NodeFilter - .property("p_u64s_max") - .max() - .eq(Prop::U64(u64::MAX)); - let expected: Vec<&str> = vec!["n5", "n1"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s_min") - .min() - .eq(Prop::U64(u64::MIN)); - let expected: Vec<&str> = vec!["n5"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_u8s_max").sum().eq(Prop::U64(510)); - let expected: Vec<&str> = vec!["n1"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s_max") - .sum() - .eq(Prop::U64(131070)); - let expected: Vec<&str> = vec!["n1"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s_max") - .sum() - .eq(Prop::U64(8589934590)); - let expected: Vec<&str> = vec!["n1"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_u64s_max").sum().gt(Prop::U64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - // AVG is computed in f64 even if SUM overflowed. - let avg = (u64::MAX as f64 + 1.0) / 2.0; - let filter = NodeFilter.property("p_u64s_max").avg().eq(avg); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_i64s_max").sum().gt(Prop::I64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - // AVG is computed in f64 even if SUM overflowed. - let avg = (i64::MAX as f64 + 1.0) / 2.0; - let filter = NodeFilter.property("p_i64s_max").avg().eq(avg); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.property("p20").temporal().all().eq("Gold_ship"); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - // ------ Property: any ------ - #[test] - fn test_node_property_any() { - let filter = NodeFilter.property("p_u8s").any().eq(Prop::U8(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_property_ne() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").ne(2u64); + let expected_results = vec![ + "1->2", + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Property: all ------ - #[test] - fn test_node_property_all() { - let filter = NodeFilter - .property("p_bools_all") - .all() - .eq(Prop::Bool(true)); - let expected = vec!["n10", "n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.property("p30").temporal().first().ne("Old_boat"); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Metadata: any ------ - #[test] - fn test_node_metadata_any() { - let filter = NodeFilter.metadata("p_u64s").any().eq(Prop::U64(1)); - let expected = vec!["n10", "n7"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.property("p30").temporal().all().ne("Classic"); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - // ------ Metadata: all ------ - #[test] - fn test_node_metadata_all() { - let filter = NodeFilter.metadata("p_strs").all().eq("a"); - let expected = vec!["n6", "n7"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_property_lt() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").lt(10u64); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Temporal First: any ------ - #[test] - fn test_node_temporal_property_first_any() { - let filter = NodeFilter - .property("p_bools") - .temporal() - .first() - .any() - .eq(false); - let expected = vec!["n1", "n10", "n2", "n3", "n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.property("p2").temporal().first().lt(5u64); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Temporal First: all ------ - #[test] - fn test_node_temporal_property_first_all() { - let filter = NodeFilter - .property("p_bools_all") - .temporal() - .first() - .all() - .eq(true); - let expected = vec!["n10", "n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.property("p2").temporal().all().lt(10u64); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - // ------ Temporal last: any ------ - #[test] - fn test_node_temporal_property_last_any() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .any() - .eq(Prop::F32(3.5)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_property_le() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").le(6u64); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Temporal last: all ------ - #[test] - fn test_node_temporal_property_last_all() { - let filter = NodeFilter - .property("p_bools_all") - .temporal() - .last() - .all() - .eq(true); - let expected = vec!["n10", "n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.property("p2").temporal().first().le(3u64); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Temporal Any: any ------ - #[test] - fn test_node_temporal_property_any_any() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .any() - .eq(Prop::F32(3.5)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .any() - .eq(Prop::F32(30.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.property("p2").temporal().all().le(5u64); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - // ------ Temporal Any: all ------ - #[test] - fn test_node_temporal_property_any_all() { - let filter = NodeFilter - .property("p_bools") - .temporal() - .any() - .all() - .eq(false); - let expected = vec!["n2", "n4"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_property_gt() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").gt(2u64); + let expected_results = vec![ + "1->2", + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_nested_list_property_all_all_all_any() { - let filter = NodeFilter - .property("nested_list") - .all() - .all() - .all() - .any() - .gt(45.0); - - let expected = vec!["n1", "n3"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.property("p2").temporal().first().gt(5u64); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_node_nested_list_temporal_property_all_all_all_all_any() { - let filter = NodeFilter - .property("nested_list") - .temporal() - .all() - .all() - .all() - .all() - .any() - .gt(45.0); - - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } + let filter = EdgeFilter.property("p2").temporal().all().gt(5u64); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - // ------ Temporal All: any ------ - #[test] - fn test_node_temporal_property_all_any() { - let filter = NodeFilter - .property("p_bools") - .temporal() - .all() - .any() - .eq(true); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } + #[test] + fn test_filter_edges_for_property_ge() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").ge(2u64); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - // ------ Temporal All: all ------ - #[test] - fn test_node_temporal_property_all_all() { - let filter = NodeFilter - .property("p_bools_all") - .temporal() - .all() - .all() - .eq(true); - let expected = vec!["n4", "n10"]; - apply_assertion(filter, &expected); - } -} + let filter = EdgeFilter.property("p2").temporal().first().ge(6u64); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); -#[cfg(test)] -mod test_edge_filter { - use crate::{ - init_edges_graph, init_edges_graph_with_num_ids, init_edges_graph_with_str_ids, - init_edges_graph_with_str_ids_del, init_nodes_graph, IdentityGraphTransformer, - }; - use raphtory::db::graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, assert_select_edges_results, - TestGraphVariants, TestVariants, - }, - views::filter::model::{ - edge_filter::EdgeFilter, - node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, - property_filter::ops::{ListAggOps, PropertyFilterOps}, - ComposableFilter, EdgeViewFilterOps, PropertyFilterFactory, - TemporalPropertyFilterFactory, ViewWrapOps, - }, - }; + let filter = EdgeFilter.property("p2").temporal().all().ge(6u64); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_src_property_eq() { - let filter = EdgeFilter::src().property("p10").eq("Paper_airplane"); - let expected_results = vec!["1->2", "3->1"]; - let g = |g| init_edges_graph(init_nodes_graph(g)); - assert_filter_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_for_property_in() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").is_in(vec![Prop::U64(6)]); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_src_property_temporal_eq() { - let filter = EdgeFilter::src() - .property("p30") - .temporal() - .first() - .eq("Old_boat"); - let expected_results = vec!["2->1", "2->3"]; - let g = |g| init_edges_graph(init_nodes_graph(g)); - assert_filter_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p2") + .is_in(vec![Prop::U64(2), Prop::U64(6)]); + let expected_results = vec![ + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_src_metadata_eq() { - let filter = EdgeFilter::src().metadata("m1").eq("pometry"); - let expected_results = vec!["1->2"]; - let g = |g| init_edges_graph(init_nodes_graph(g)); - assert_filter_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p2") + .temporal() + .first() + .is_in(vec![Prop::U64(6)]); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_eq() { - let filter = EdgeFilter::src().name().eq("3"); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p2") + .temporal() + .all() + .is_in(vec![Prop::U64(6)]); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_src_ne() { - let filter = EdgeFilter::src().name().ne("1"); - let expected_results = vec![ - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_for_property_not_in() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_in() { - let filter = EdgeFilter::src().name().is_in(vec!["1"]); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().is_in(vec!["1", "2"]); - let expected_results = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p2") + .temporal() + .first() + .is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_not_in() { - let filter = EdgeFilter::src().name().is_not_in(vec!["1"]); - let expected_results = vec![ - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p2") + .temporal() + .all() + .is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_dst_eq() { - let filter = EdgeFilter::dst().name().eq("2"); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_for_property_is_some() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").is_some(); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_dst_ne() { - let filter = EdgeFilter::dst().name().ne("2"); - let expected_results = vec![ - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter.property("p2").temporal().first().is_some(); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_dst_in() { - let filter = EdgeFilter::dst().name().is_in(vec!["2"]); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().is_in(vec!["2", "3"]); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_for_property_is_none() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. + let filter = EdgeFilter.property("p2").is_none(); + let expected_results = Vec::<&str>::new(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); - #[test] - fn test_filter_edges_for_dst_not_in() { - let filter = EdgeFilter::dst().name().is_not_in(vec!["1"]); - let expected_results = vec![ - "1->2", - "2->3", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter.property("p2").temporal().first().is_none(); + let expected_results = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_src_dst_starts_with() { - let filter = EdgeFilter::src().name().starts_with("Joh"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().starts_with("Joker"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().starts_with("Jimmy"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().starts_with("Tango"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_for_property_starts_with() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p10").starts_with("Pa"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_dst_ends_with() { - let filter = EdgeFilter::src().name().ends_with("Mayer"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().ends_with("Cruise"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().ends_with("Page"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().ends_with("Cruise"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .any() + .starts_with("Pape"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_contains() { - let filter = EdgeFilter::src().name().contains("Mayer"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .starts_with("Paper"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_contains_not() { - let filter = EdgeFilter::src().name().not_contains("Mayer"); - let expected_results: Vec<&str> = - vec!["1->2", "2->1", "2->3", "3->1", "David Gilmour->John Mayer"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .starts_with("Traffic"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_fuzzy_search() { - let filter = EdgeFilter::src().name().fuzzy_search("John", 2, true); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().fuzzy_search("John", 2, false); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().fuzzy_search("John May", 2, false); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p30") + .temporal() + .first() + .starts_with("Old"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_not_src() { - let filter = EdgeFilter::src().name().is_not_in(vec!["1"]).not(); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p20") + .temporal() + .all() + .starts_with("Gold"); + let expected_results: Vec<&str> = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_src_id_eq() { - let filter = EdgeFilter::src().id().eq("3"); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().id().eq(3); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_for_property_ends_with() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p10").ends_with("lane"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_dst_id_eq() { - let filter = EdgeFilter::dst().id().eq("3"); - let expected_results = vec!["2->3"]; - // assert_filter_edges_results( - // init_edges_graph, - // IdentityGraphTransformer, - // filter.clone(), - // &expected_results, - // TestVariants::All, - // ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().id().eq(3); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .any() + .ends_with("ship"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_id_ne() { - let filter = EdgeFilter::src().id().ne("3"); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().id().ne(3); - let expected_results = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .ends_with("ane"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_dst_id_ne() { - let filter = EdgeFilter::dst().id().ne("3"); - let expected_results = vec![ - "1->2", - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().id().ne(3); - let expected_results = vec!["1->2", "2->1", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .ends_with("marcus"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_id_src_is_in() { - let filter = EdgeFilter::src().id().is_in(vec!["3"]); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().id().is_in(vec![3]); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p20") + .temporal() + .first() + .ends_with("boat"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_id_dst_is_in() { - let filter = EdgeFilter::dst().id().is_in(vec!["3"]); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().id().is_in(vec![3]); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p20") + .temporal() + .all() + .ends_with("ship"); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_id_src_is_not_in() { - let filter = EdgeFilter::src().id().is_not_in(vec!["3"]); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().id().is_not_in(vec![3]); - let expected_results = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_for_property_contains() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p10").contains("Paper"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_id_dst_is_not_in() { - let filter = EdgeFilter::dst().id().is_not_in(vec!["3"]); - let expected_results = vec![ - "1->2", - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().id().is_not_in(vec![3]); - let expected_results = vec!["1->2", "2->1", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .any() + .contains("Paper"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_id_lt() { - let filter = EdgeFilter::src().id().lt(3); - let expected_results = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .contains("Paper"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_dst_id_lt() { - let filter = EdgeFilter::dst().id().lt(3); - let expected_results = vec!["1->2", "2->1", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p20") + .temporal() + .first() + .contains("boat"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_id_le() { - let filter = EdgeFilter::src().id().le(3); - let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter.property("p20").temporal().all().contains("ship"); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_dst_id_le() { - let filter = EdgeFilter::dst().id().le(3); - let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_for_property_contains_not() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p10").not_contains("ship"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_id_gt() { - let filter = EdgeFilter::src().id().gt(1); - let expected_results = vec!["2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .any() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_dst_id_gt() { - let filter = EdgeFilter::dst().id().gt(1); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_src_id_ge() { - let filter = EdgeFilter::src().id().ge(1); - let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p20") + .temporal() + .first() + .not_contains("boat"); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_dst_id_ge() { - let filter = EdgeFilter::dst().id().ge(1); - let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p30") + .temporal() + .all() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_src_name_starts_with() { - let filter = EdgeFilter::src().name().starts_with("Tw"); - let expected_results = vec!["Two->One", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_by_fuzzy_search() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. + // TODO: Enable these test for event_disk_graph, persistent_disk_graph once string property is fixed. + let filter = EdgeFilter.property("p1").fuzzy_search("shiv", 2, true); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); - #[test] - fn test_filter_edges_for_src_id_ends_with() { - let filter = EdgeFilter::src().id().ends_with("don"); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter.property("p1").fuzzy_search("ShiV", 2, true); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); - #[test] - fn test_filter_edges_for_src_id_contains() { - let filter = EdgeFilter::src().id().contains("don"); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter.property("p1").fuzzy_search("shiv", 2, false); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_filter_edges_for_dst_id_contains() { - let filter = EdgeFilter::dst().id().contains("Par"); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_filter_edges_for_not_property() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. + let filter = EdgeFilter.property("p2").ne(2u64).not(); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_filter_edges_for_dst_name_not_contains() { - let filter = EdgeFilter::dst().name().not_contains("Par"); - let expected_results = vec![ - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - "Three->One", - "Two->One", - "Two->Three", - ]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_window_filter() { + let filter = EdgeFilter + .window(1, 3) + .property("p2") + .temporal() + .sum() + .ge(2u64); - #[test] - fn test_filter_edges_for_src_id_is_in() { - let filter = EdgeFilter::src().id().is_in(["Two"]); - let expected_results = vec!["Two->One", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_dst_id_is_not_in() { - let filter = EdgeFilter::dst().id().is_not_in(["One"]); - let expected_results = vec![ - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - "London->Paris", - "Two->Three", - ]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .window(1, 5) + .property("p2") + .temporal() + .sum() + .ge(2u64); - #[test] - fn test_is_active_edge_window() { - let filter = EdgeFilter.window(1, 3).is_active(); - let expected_results = vec!["London->Paris", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let expected_results = vec![ + "1->2", + "2->3", + "3->1", + "2->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_is_active_edge_after() { - let filter = EdgeFilter.after(3).is_active(); - let expected_results = vec![ - "Bangalore->Bangalore", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_window_filter_on_non_temporal_property() { + let filter1 = EdgeFilter.window(1, 2).property("p1").eq("shivam_kapoor"); + let filter2 = EdgeFilter + .window(100, 200) + .property("p1") + .eq("shivam_kapoor"); - #[test] - fn test_is_active_edge_before() { - let filter = EdgeFilter.before(3).is_active(); - let expected_results = vec!["London->Paris", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter1.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter1.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_is_active_edge_snapshot_latest() { - let filter = EdgeFilter.snapshot_latest().is_active(); - let expected_results = vec!["Bangalore->Bangalore"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + let expected_results = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::EventOnly, + ); - #[test] - fn test_is_valid_edge_window() { - let filter = EdgeFilter.window(1, 3).is_valid(); - let expected_results = vec!["London->Paris", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - - let filter = EdgeFilter.window(1, 4).is_valid(); - let expected_results = vec!["Three->One", "Two->One", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - } + let filter2 = EdgeFilter + .window(100, 200) + .property("p1") + .eq("shivam_kapoor"); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_is_valid_edge_snapshot_at() { - let filter = EdgeFilter.snapshot_at(2).is_valid(); - let expected_results = vec!["London->Paris", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - - let filter = EdgeFilter.snapshot_at(3).is_valid(); - let expected_results = vec!["Three->One", "Two->One", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - } + #[test] + fn test_edges_window_filter_any_all_over_window() { + let filter_any = EdgeFilter + .window(2, 4) + .property("p20") + .temporal() + .any() + .eq("Gold_boat"); + + let expected_any = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_any.clone(), + &expected_any, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_any.clone(), + &expected_any, + TestVariants::All, + ); - #[test] - fn test_is_valid_edge_snapshot_latest() { - let filter = EdgeFilter.snapshot_latest().is_valid(); - let expected_results = vec![ - "Bangalore->Bangalore", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - "Three->One", - "Two->One", - "Two->Three", - ]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - } + let filter_all = EdgeFilter + .window(2, 4) + .property("p20") + .temporal() + .all() + .eq("Gold_boat"); - // Disk graph doesn't support deletions - #[test] - fn test_is_deleted_edge_after() { - let filter = EdgeFilter.after(1).is_deleted(); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let expected_all: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_all.clone(), + &expected_all, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_all.clone(), + &expected_all, + TestVariants::All, + ); + } - // Disk graph doesn't support deletions - #[test] - fn test_is_deleted_edge_before() { - let filter = EdgeFilter.before(4).is_deleted(); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_window_filter_and() { + let filter1 = EdgeFilter + .window(3, 6) + .property("p10") + .temporal() + .any() + .eq("Paper_airplane"); - #[test] - fn test_is_self_loop_edge_window() { - let filter = EdgeFilter.window(1, 3).is_self_loop(); - let expected_results = vec![]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.window(1, 6).is_self_loop(); - let expected_results = vec!["Bangalore->Bangalore"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } -} + let filter2 = EdgeFilter + .window(3, 6) + .property("p2") + .temporal() + .sum() + .eq(6u64); -#[cfg(test)] -mod test_edge_property_filter { - use crate::{init_edges_graph, init_edges_graph2, IdentityGraphTransformer}; - use raphtory::db::graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, - TestVariants, - }, - views::filter::model::{ - edge_filter::EdgeFilter, - property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, - ComposableFilter, PropertyFilterFactory, TemporalPropertyFilterFactory, ViewWrapOps, - }, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - #[test] - fn test_filter_edges_for_property_eq() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").eq(2u64); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p30").temporal().first().eq("Old_boat"); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p20").temporal().all().eq("Gold_ship"); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = filter1.and(filter2); + + let expected_results = vec!["2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_property_ne() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").ne(2u64); - let expected_results = vec![ - "1->2", - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p30").temporal().first().ne("Old_boat"); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p30").temporal().all().ne("Classic"); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_layer_filter() { + let filter = EdgeFilter + .layer("fire_nation") + .property("p2") + .temporal() + .sum() + .ge(2u64); - #[test] - fn test_filter_edges_for_property_lt() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").lt(10u64); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().lt(5u64); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().all().lt(10u64); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let expected_results = vec!["1->2", "3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_property_le() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").le(6u64); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().le(3u64); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().all().le(5u64); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_at_filter() { + // Only time=2 contributes; edge 2->3 has p2=2 at t=2 + let filter = EdgeFilter.at(2).property("p2").temporal().sum().eq(2u64); - #[test] - fn test_filter_edges_for_property_gt() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").gt(2u64); - let expected_results = vec![ - "1->2", - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().gt(5u64); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().all().gt(5u64); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_property_ge() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").ge(2u64); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().ge(6u64); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().all().ge(6u64); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + // Only time=3 contributes; edge 3->1 has p2=6 at t=3 + let filter = EdgeFilter.at(3).property("p2").temporal().sum().eq(6u64); - #[test] - fn test_filter_edges_for_property_in() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").is_in(vec![Prop::U64(6)]); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .is_in(vec![Prop::U64(2), Prop::U64(6)]); - let expected_results = vec![ - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .temporal() - .first() - .is_in(vec![Prop::U64(6)]); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .temporal() - .all() - .is_in(vec![Prop::U64(6)]); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let expected_results = vec!["3->1", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_property_not_in() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .temporal() - .first() - .is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .temporal() - .all() - .is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_after_filter() { + // after(2) means t >= 3 + let filter = EdgeFilter.after(2).property("p2").temporal().sum().ge(6u64); - #[test] - fn test_filter_edges_for_property_is_some() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").is_some(); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().is_some(); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + // At t=3: 3->1 and 2->1 have p2=6 + // At t=4: David->John and John->Jimmy have p2=6 + let expected_results = vec![ + "3->1", + "2->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_property_is_none() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. - let filter = EdgeFilter.property("p2").is_none(); - let expected_results = Vec::<&str>::new(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("p2").temporal().first().is_none(); - let expected_results = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_before_filter() { + // before(3) means t <= 2 + let filter = EdgeFilter + .before(3) + .property("p2") + .temporal() + .sum() + .eq(2u64); + + // Only t=2 contributes for p2=2 -> 2->3 + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edges_for_property_starts_with() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p10").starts_with("Pa"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .any() - .starts_with("Pape"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .starts_with("Paper"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .starts_with("Traffic"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p30") - .temporal() - .first() - .starts_with("Old"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .all() - .starts_with("Gold"); - let expected_results: Vec<&str> = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + // And p2=6 edges shouldn't match, because their p2=6 lives at t=3+. + let filter = EdgeFilter + .before(3) + .property("p2") + .temporal() + .sum() + .eq(6u64); - #[test] - fn test_filter_edges_for_property_ends_with() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p10").ends_with("lane"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .any() - .ends_with("ship"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .ends_with("ane"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .ends_with("marcus"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .first() - .ends_with("boat"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .all() - .ends_with("ship"); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let expected_results = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_filter_edges_for_property_contains() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p10").contains("Paper"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .any() - .contains("Paper"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .contains("Paper"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .first() - .contains("boat"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p20").temporal().all().contains("ship"); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_latest_filter() { + // At latest time (currently t=4), only the t=4 edges exist in the Event graph. + // Use EventOnly so the expectation is stable and matches node-style. + let filter = EdgeFilter.latest().property("p2").eq(6u64); - #[test] - fn test_filter_edges_for_property_contains_not() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p10").not_contains("ship"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .any() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .first() - .not_contains("boat"); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p30") - .temporal() - .all() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let expected_results = vec!["David Gilmour->John Mayer", "John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_filter_edges_by_fuzzy_search() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. - // TODO: Enable these test for event_disk_graph, persistent_disk_graph once string property is fixed. - let filter = EdgeFilter.property("p1").fuzzy_search("shiv", 2, true); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = EdgeFilter.property("p1").fuzzy_search("ShiV", 2, true); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = EdgeFilter.property("p1").fuzzy_search("shiv", 2, false); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - } + #[test] + fn test_edges_snapshot_at_semantics_event_graph() { + let t = 2; + + let filter_snapshot = EdgeFilter + .snapshot_at(t) + .property("p2") + .temporal() + .sum() + .eq(2u64); + + let filter_before = EdgeFilter + .before(t + 1) + .property("p2") + .temporal() + .sum() + .eq(2u64); + + let expected_results = vec!["2->3"]; + + // snapshot_at + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot, + &expected_results, + TestVariants::EventOnly, + ); - #[test] - fn test_filter_edges_for_not_property() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. - let filter = EdgeFilter.property("p2").ne(2u64).not(); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } + // before(t+1) + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_before.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_before, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_window_filter() { - let filter = EdgeFilter - .window(1, 3) - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .window(1, 5) - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec![ - "1->2", - "2->3", - "3->1", - "2->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_snapshot_at_semantics_persistent_graph() { + let t = 2; - #[test] - fn test_edges_window_filter_on_non_temporal_property() { - let filter1 = EdgeFilter.window(1, 2).property("p1").eq("shivam_kapoor"); - let filter2 = EdgeFilter - .window(100, 200) - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter1.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter1.clone(), - &expected_results, - TestVariants::All, - ); - - let expected_results = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter2 = EdgeFilter - .window(100, 200) - .property("p1") - .eq("shivam_kapoor"); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + let filter_snapshot = EdgeFilter + .snapshot_at(t) + .property("p2") + .temporal() + .sum() + .eq(2u64); - #[test] - fn test_edges_window_filter_any_all_over_window() { - let filter_any = EdgeFilter - .window(2, 4) - .property("p20") - .temporal() - .any() - .eq("Gold_boat"); - - let expected_any = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_any.clone(), - &expected_any, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_any.clone(), - &expected_any, - TestVariants::All, - ); - - let filter_all = EdgeFilter - .window(2, 4) - .property("p20") - .temporal() - .all() - .eq("Gold_boat"); - - let expected_all: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_all.clone(), - &expected_all, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_all.clone(), - &expected_all, - TestVariants::All, - ); - } + let filter_at = EdgeFilter.at(t).property("p2").temporal().sum().eq(2u64); - #[test] - fn test_edges_window_filter_and() { - let filter1 = EdgeFilter - .window(3, 6) - .property("p10") - .temporal() - .any() - .eq("Paper_airplane"); - - let filter2 = EdgeFilter - .window(3, 6) - .property("p2") - .temporal() - .sum() - .eq(6u64); - - let filter = filter1.and(filter2); - - let expected_results = vec!["2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + let expected_results = vec!["2->3"]; - #[test] - fn test_edges_layer_filter() { - let filter = EdgeFilter - .layer("fire_nation") - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["1->2", "3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + // snapshot_at + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot, + &expected_results, + TestVariants::PersistentOnly, + ); - #[test] - fn test_edges_at_filter() { - // Only time=2 contributes; edge 2->3 has p2=2 at t=2 - let filter = EdgeFilter.at(2).property("p2").temporal().sum().eq(2u64); + // at(t) + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_at.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_at, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + #[test] + fn test_edges_snapshot_latest_semantics_event_graph() { + let filter_snapshot_latest = EdgeFilter + .snapshot_latest() + .property("p2") + .temporal() + .sum() + .ge(6u64); - // Only time=3 contributes; edge 3->1 has p2=6 at t=3 - let filter = EdgeFilter.at(3).property("p2").temporal().sum().eq(6u64); + let filter_noop = EdgeFilter.property("p2").temporal().sum().ge(6u64); - let expected_results = vec!["3->1", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + // Across the whole event history, p2=6 appears at t=3 and t=4. + let expected_results = vec![ + "3->1", + "2->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; - #[test] - fn test_edges_after_filter() { - // after(2) means t >= 3 - let filter = EdgeFilter.after(2).property("p2").temporal().sum().ge(6u64); - - // At t=3: 3->1 and 2->1 have p2=6 - // At t=4: David->John and John->Jimmy have p2=6 - let expected_results = vec![ - "3->1", - "2->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + // snapshot_latest + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot_latest.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot_latest, + &expected_results, + TestVariants::EventOnly, + ); - #[test] - fn test_edges_before_filter() { - // before(3) means t <= 2 - let filter = EdgeFilter - .before(3) - .property("p2") - .temporal() - .sum() - .eq(2u64); - - // Only t=2 contributes for p2=2 -> 2->3 - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - // And p2=6 edges shouldn't match, because their p2=6 lives at t=3+. - let filter = EdgeFilter - .before(3) - .property("p2") - .temporal() - .sum() - .eq(6u64); - - let expected_results = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + // no-op baseline + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_noop.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_noop, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_latest_filter() { - // At latest time (currently t=4), only the t=4 edges exist in the Event graph. - // Use EventOnly so the expectation is stable and matches node-style. - let filter = EdgeFilter.latest().property("p2").eq(6u64); + #[test] + fn test_edges_snapshot_latest_semantics_persistent_graph() { + let filter_snapshot_latest = EdgeFilter.snapshot_latest().property("p2").eq(6u64); - let expected_results = vec!["David Gilmour->John Mayer", "John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + let filter_latest = EdgeFilter.latest().property("p2").eq(6u64); - #[test] - fn test_edges_snapshot_at_semantics_event_graph() { - let t = 2; - - let filter_snapshot = EdgeFilter - .snapshot_at(t) - .property("p2") - .temporal() - .sum() - .eq(2u64); - - let filter_before = EdgeFilter - .before(t + 1) - .property("p2") - .temporal() - .sum() - .eq(2u64); - - let expected_results = vec!["2->3"]; - - // snapshot_at - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot, - &expected_results, - TestVariants::EventOnly, - ); - - // before(t+1) - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_before.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_before, - &expected_results, - TestVariants::EventOnly, - ); - } + // In persistent latest state at t=4, these edges have p2=6: + // - t=3 edges: 3->1, 2->1 + // - t=4 edges: David->John, John->Jimmy + let expected_results = vec![ + "3->1", + "2->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; - #[test] - fn test_edges_snapshot_at_semantics_persistent_graph() { - let t = 2; + // snapshot_latest + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot_latest.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot_latest, + &expected_results, + TestVariants::PersistentOnly, + ); - let filter_snapshot = EdgeFilter - .snapshot_at(t) - .property("p2") - .temporal() - .sum() - .eq(2u64); + // latest + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_latest.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_latest, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter_at = EdgeFilter.at(t).property("p2").temporal().sum().eq(2u64); + #[test] + fn test_edges_layer_then_window_ordering() { + // In layer "fire_nation" within window [1,3), edge 1->2 matches p1 == "shivam_kapoor". + let filter = EdgeFilter + .layer("fire_nation") + .window(1, 3) + .property("p1") + .eq("shivam_kapoor"); - let expected_results = vec!["2->3"]; + let expected_results = vec!["1->2"]; - // snapshot_at - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot, - &expected_results, - TestVariants::PersistentOnly, - ); - - // at(t) - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_at.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_at, - &expected_results, - TestVariants::PersistentOnly, - ); - } + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_edges_snapshot_latest_semantics_event_graph() { - let filter_snapshot_latest = EdgeFilter - .snapshot_latest() - .property("p2") - .temporal() - .sum() - .ge(6u64); - - let filter_noop = EdgeFilter.property("p2").temporal().sum().ge(6u64); - - // Across the whole event history, p2=6 appears at t=3 and t=4. - let expected_results = vec![ - "3->1", - "2->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; + #[test] + fn test_edges_window_then_layer_ordering() { + // Same semantics, reversed chaining order. + let filter = EdgeFilter + .window(1, 3) + .layer("fire_nation") + .property("p1") + .eq("shivam_kapoor"); - // snapshot_latest - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot_latest.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot_latest, - &expected_results, - TestVariants::EventOnly, - ); - - // no-op baseline - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_noop.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_noop, - &expected_results, - TestVariants::EventOnly, - ); - } + let expected_results = vec!["1->2"]; - #[test] - fn test_edges_snapshot_latest_semantics_persistent_graph() { - let filter_snapshot_latest = EdgeFilter.snapshot_latest().property("p2").eq(6u64); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } - let filter_latest = EdgeFilter.latest().property("p2").eq(6u64); + #[test] + fn test_edges_latest_layer() { + let filter = EdgeFilter + .latest() + .layer("fire_nation") + .property("p2") + .temporal() + .last() + .eq(7u64); - // In persistent latest state at t=4, these edges have p2=6: - // - t=3 edges: 3->1, 2->1 - // - t=4 edges: David->John, John->Jimmy - let expected_results = vec![ - "3->1", - "2->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; + let expected_results = vec![]; - // snapshot_latest - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot_latest.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot_latest, - &expected_results, - TestVariants::PersistentOnly, - ); - - // latest - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_latest.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_latest, - &expected_results, - TestVariants::PersistentOnly, - ); - } + assert_filter_edges_results( + init_edges_graph2, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_layer_then_window_ordering() { - // In layer "fire_nation" within window [1,3), edge 1->2 matches p1 == "shivam_kapoor". - let filter = EdgeFilter - .layer("fire_nation") - .window(1, 3) - .property("p1") - .eq("shivam_kapoor"); + #[test] + fn test_edges_layer_latest() { + let filter = EdgeFilter + .layer("fire_nation") + .latest() + .property("p2") + .temporal() + .last() + .eq(7u64); - let expected_results = vec!["1->2"]; + let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); + assert_filter_edges_results( + init_edges_graph2, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } } - #[test] - fn test_edges_window_then_layer_ordering() { - // Same semantics, reversed chaining order. - let filter = EdgeFilter - .window(1, 3) - .layer("fire_nation") - .property("p1") - .eq("shivam_kapoor"); + #[cfg(test)] + mod test_edge_composite_filter { + use crate::test::{init_edges_graph, IdentityGraphTransformer}; + use raphtory::db::graph::{ + assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, + TestVariants, + }, + views::filter::model::{ + edge_filter::EdgeFilter, node_filter::ops::NodeFilterOps, + property_filter::ops::PropertyFilterOps, ComposableFilter, PropertyFilterFactory, + TryAsCompositeFilter, + }, + }; + + #[test] + fn test_filter_edge_for_src_dst() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter::src() + .name() + .eq("3") + .and(EdgeFilter::dst().name().eq("1")); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - let expected_results = vec!["1->2"]; + #[test] + fn test_unique_results_from_composite_filters() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter + .property("p2") + .ge(2u64) + .and(EdgeFilter.property("p2").ge(1u64)); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter + .property("p2") + .ge(2u64) + .or(EdgeFilter.property("p2").ge(5u64)); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_latest_layer() { - let filter = EdgeFilter - .latest() - .layer("fire_nation") - .property("p2") - .temporal() - .last() - .eq(7u64); + #[test] + fn test_composite_filter_edges() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. + // TODO: Enable these test for event_disk_graph, persistent_disk_graph once string property is fixed. + let filter = EdgeFilter + .property("p2") + .eq(2u64) + .and(EdgeFilter.property("p1").eq("kapoor")); + let expected_results = Vec::<&str>::new(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - let expected_results = vec![]; + let filter = EdgeFilter + .property("p2") + .eq(2u64) + .or(EdgeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - assert_filter_edges_results( - init_edges_graph2, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } + let filter = EdgeFilter.property("p1").eq("pometry").or(EdgeFilter + .property("p2") + .eq(6u64) + .and(EdgeFilter.property("p3").eq(1u64))); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_edges_layer_latest() { - let filter = EdgeFilter - .layer("fire_nation") - .latest() - .property("p2") - .temporal() - .last() - .eq(7u64); + let filter = EdgeFilter::src() + .name() + .eq("13") + .and(EdgeFilter.property("p1").eq("prop1")); + let expected_results = Vec::<&str>::new(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - let expected_results = vec!["1->2"]; + let filter = EdgeFilter + .property("p2") + .eq(4u64) + .and(EdgeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - assert_filter_edges_results( - init_edges_graph2, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } -} + let filter = EdgeFilter::src() + .name() + .eq("1") + .and(EdgeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); -#[cfg(test)] -mod test_edge_composite_filter { - use crate::{init_edges_graph, IdentityGraphTransformer}; - use raphtory::db::graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, - TestVariants, - }, - views::filter::model::{ - edge_filter::EdgeFilter, node_filter::ops::NodeFilterOps, - property_filter::ops::PropertyFilterOps, ComposableFilter, PropertyFilterFactory, - TryAsCompositeFilter, - }, - }; + let filter = EdgeFilter::dst() + .name() + .eq("1") + .and(EdgeFilter.property("p2").eq(6u64)); + let expected_results = vec!["2->1", "3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_filter_edge_for_src_dst() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter::src() - .name() - .eq("3") - .and(EdgeFilter::dst().name().eq("1")); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + let filter = EdgeFilter::src() + .name() + .eq("1") + .and(EdgeFilter.property("p1").eq("shivam_kapoor")) + .or(EdgeFilter.property("p3").eq(5u64)); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); - #[test] - fn test_unique_results_from_composite_filters() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter - .property("p2") - .ge(2u64) - .and(EdgeFilter.property("p2").ge(1u64)); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter - .property("p2") - .ge(2u64) - .or(EdgeFilter.property("p2").ge(5u64)); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_composite_filter_edges() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. - // TODO: Enable these test for event_disk_graph, persistent_disk_graph once string property is fixed. - let filter = EdgeFilter - .property("p2") - .eq(2u64) - .and(EdgeFilter.property("p1").eq("kapoor")); - let expected_results = Vec::<&str>::new(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .eq(2u64) - .or(EdgeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p1").eq("pometry").or(EdgeFilter - .property("p2") - .eq(6u64) - .and(EdgeFilter.property("p3").eq(1u64))); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src() - .name() - .eq("13") - .and(EdgeFilter.property("p1").eq("prop1")); - let expected_results = Vec::<&str>::new(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .eq(4u64) - .and(EdgeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src() - .name() - .eq("1") - .and(EdgeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst() - .name() - .eq("1") - .and(EdgeFilter.property("p2").eq(6u64)); - let expected_results = vec!["2->1", "3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src() - .name() - .eq("1") - .and(EdgeFilter.property("p1").eq("shivam_kapoor")) - .or(EdgeFilter.property("p3").eq(5u64)); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_not_composite_filter_edges() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. + let filter = EdgeFilter::src() + .name() + .eq("13") + .and(EdgeFilter.property("p1").eq("prop1")) + .not(); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); - #[test] - fn test_not_composite_filter_edges() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. - let filter = EdgeFilter::src() - .name() - .eq("13") - .and(EdgeFilter.property("p1").eq("prop1")) - .not(); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter::src() - .name() - .eq("13") - .and(EdgeFilter.property("p1").eq("prop1").not()) - .not(); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); + let filter = EdgeFilter::src() + .name() + .eq("13") + .and(EdgeFilter.property("p1").eq("prop1").not()) + .not(); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } } } diff --git a/raphtory/tests/test_layers.rs b/raphtory/tests/test_layers.rs index 880b96f8cb..c9a9538d6f 100644 --- a/raphtory/tests/test_layers.rs +++ b/raphtory/tests/test_layers.rs @@ -1,783 +1,787 @@ -use itertools::Itertools; -use proptest::proptest; -use raphtory::{ - db::graph::{graph::assert_graph_equal, views::deletion_graph::PersistentGraph}, - prelude::*, - test_storage, - test_utils::{build_graph, build_graph_layer, build_graph_strat, GraphFixture}, -}; -use raphtory_api::core::entities::GID; -use serde_json::json; - -#[test] -fn prop_test_layering() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, false), layer in proptest::sample::subsequence(&["_default", "a", "b"], 0..3))| { +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use proptest::proptest; + use raphtory::{ + db::graph::{graph::assert_graph_equal, views::deletion_graph::PersistentGraph}, + prelude::*, + test_storage, + test_utils::{build_graph, build_graph_layer, build_graph_strat, GraphFixture}, + }; + use raphtory_api::core::entities::GID; + use serde_json::json; + + #[test] + fn prop_test_layering() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, false), layer in proptest::sample::subsequence(&["_default", "a", "b"], 0..3))| { + let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); + let g = Graph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer.clone()); + assert_graph_equal(&g_layer, &g_layer_expected); + }) + } + + #[test] + fn test_node_explicit_node_additions() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{"10":{"props":{"t_props":[[0,[]]],"c_props":[]},"node_type":null}},"edges":[]})).unwrap(); + let layer = []; let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); - assert_graph_equal(&g_layer, &g_layer_expected); - }) -} + let g_layer = g.valid_layers(layer.clone()); -#[test] -fn test_node_explicit_node_additions() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{"10":{"props":{"t_props":[[0,[]]],"c_props":[]},"node_type":null}},"edges":[]})).unwrap(); - let layer = []; - let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); - let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); + assert_graph_equal(&g_layer, &g_layer_expected); + } - assert_graph_equal(&g_layer, &g_layer_expected); -} + #[test] + fn test_failure() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[3,9,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[9,3,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let layer = ["_default", "b"]; + let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); + let g = Graph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer.clone()); -#[test] -fn test_failure() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[3,9,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[9,3,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let layer = ["_default", "b"]; - let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); - let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); + assert_graph_equal(&g_layer, &g_layer_expected); + } - assert_graph_equal(&g_layer, &g_layer_expected); -} + #[test] + fn test_failure2() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let layer = ["_default", "b"]; + let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); + let g = Graph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer.clone()); -#[test] -fn test_failure2() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let layer = ["_default", "b"]; - let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); - let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); + assert_graph_equal(&g_layer, &g_layer_expected); + } - assert_graph_equal(&g_layer, &g_layer_expected); -} + #[test] + fn test_failure3() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,1,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let layer = ["_default", "b"]; + let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); + let g = Graph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer.clone()); -#[test] -fn test_failure3() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,1,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let layer = ["_default", "b"]; - let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); - let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); + assert_graph_equal(&g_layer, &g_layer_expected); + } - assert_graph_equal(&g_layer, &g_layer_expected); -} + #[test] + fn prop_test_layering_persistent_graph() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), layer in proptest::sample::subsequence(&["_default", "a", "b"], 0..3))| { + let g_layer_expected = PersistentGraph::from(build_graph_layer(&graph_f, &layer)); + let g = PersistentGraph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer); + assert_graph_equal(&g_layer, &g_layer_expected); + }) + } -#[test] -fn prop_test_layering_persistent_graph() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), layer in proptest::sample::subsequence(&["_default", "a", "b"], 0..3))| { - let g_layer_expected = PersistentGraph::from(build_graph_layer(&graph_f, &layer)); - let g = PersistentGraph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer); - assert_graph_equal(&g_layer, &g_layer_expected); - }) -} + #[test] + fn test_layer_node() { + let graph = Graph::new(); -#[test] -fn test_layer_node() { - let graph = Graph::new(); - - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(0, 2, 3, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(3, 2, 4, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 4, 1, NO_PROPS, Some("layer3")).unwrap(); - - test_storage!(&graph, |graph| { - let neighbours = graph - .layers(vec!["layer1", "layer2"]) - .unwrap() - .node(1) - .unwrap() - .neighbours() - .into_iter() - .collect_vec(); - assert_eq!( - neighbours[0] - .layers("layer2") + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(0, 2, 3, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(3, 2, 4, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 4, 1, NO_PROPS, Some("layer3")).unwrap(); + + test_storage!(&graph, |graph| { + let neighbours = graph + .layers(vec!["layer1", "layer2"]) + .unwrap() + .node(1) + .unwrap() + .neighbours() + .into_iter() + .collect_vec(); + assert_eq!( + neighbours[0] + .layers("layer2") + .unwrap() + .edges() + .id() + .collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + assert_eq!( + graph + .layers("layer2") + .unwrap() + .node(neighbours[0].name()) + .unwrap() + .edges() + .id() + .collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + let mut edges = graph + .layers("layer1") + .unwrap() + .node(neighbours[0].name()) .unwrap() .edges() .id() - .collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - assert_eq!( - graph - .layers("layer2") + .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) + .collect_vec(); + edges.sort(); + assert_eq!(edges, vec![(1, 2), (2, 4)]); + let mut edges = graph + .layers("layer1") .unwrap() - .node(neighbours[0].name()) + .edges() + .id() + .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) + .collect_vec(); + edges.sort(); + assert_eq!(edges, vec![(1, 2), (2, 4)]); + let mut edges = graph + .layers(vec!["layer1", "layer2"]) .unwrap() .edges() .id() - .collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - let mut edges = graph - .layers("layer1") - .unwrap() - .node(neighbours[0].name()) - .unwrap() - .edges() - .id() - .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) - .collect_vec(); - edges.sort(); - assert_eq!(edges, vec![(1, 2), (2, 4)]); - let mut edges = graph - .layers("layer1") - .unwrap() - .edges() - .id() - .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) - .collect_vec(); - edges.sort(); - assert_eq!(edges, vec![(1, 2), (2, 4)]); - let mut edges = graph - .layers(vec!["layer1", "layer2"]) - .unwrap() - .edges() - .id() - .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) - .collect_vec(); - edges.sort(); - assert_eq!(edges, vec![(1, 2), (2, 3), (2, 4)]); - - let mut edges = graph - .layers(["layer1", "layer3"]) - .unwrap() - .window(0, 2) - .edges() - .id() - .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) - .collect_vec(); - edges.sort(); - assert_eq!(edges, vec![(1, 2), (4, 1)]); - }); -} - -#[test] -fn layering_tests() { - let graph = Graph::new(); - let e1 = graph.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("2")).unwrap(); - - println!("edge: {e1:?}"); - // FIXME: this is weird, see issue #1458 - assert!(e1.has_layer("2")); - let history = e1.layers("2").unwrap().history(); - println!("history: {:?}", history); - assert!(e1.layers("2").unwrap().history().is_empty()); - - test_storage!(&graph, |graph| { - let e = graph.edge(1, 2).unwrap(); - // layers with non-existing layers errors - assert!(e.layers(["1", "3"]).is_err()); - // valid_layers ignores non-existing layers - assert_eq!(e.valid_layers(["1", "3"]).layer_names(), ["1"]); - assert!(e.has_layer("1")); - assert!(e.has_layer("2")); - assert!(!e.has_layer("3")); - assert!(e.valid_layers("1").has_layer("1")); - assert!(!e.valid_layers("1").has_layer("2")); - }); -} + .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) + .collect_vec(); + edges.sort(); + assert_eq!(edges, vec![(1, 2), (2, 3), (2, 4)]); -pub mod test_filters_layer_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::GraphTransformer, - views::{layer_graph::LayeredGraph, window_graph::WindowedGraph}, - }, - }, - prelude::{LayerOps, TimeOps}, - }; - use std::ops::Range; - - struct LayeredGraphTransformer(Vec); - - impl GraphTransformer for LayeredGraphTransformer { - type Return = LayeredGraph; - fn apply(&self, graph: G) -> Self::Return { - graph.layers(self.0.clone()).unwrap() - } + let mut edges = graph + .layers(["layer1", "layer3"]) + .unwrap() + .window(0, 2) + .edges() + .id() + .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) + .collect_vec(); + edges.sort(); + assert_eq!(edges, vec![(1, 2), (4, 1)]); + }); } - struct LayeredGraphWindowTransformer(Vec, Range); - - impl GraphTransformer for LayeredGraphWindowTransformer { - type Return = WindowedGraph>; - fn apply(&self, graph: G) -> Self::Return { - graph - .layers(self.0.clone()) - .unwrap() - .window(self.1.start, self.1.end) - } + #[test] + fn layering_tests() { + let graph = Graph::new(); + let e1 = graph.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("2")).unwrap(); + + println!("edge: {e1:?}"); + // FIXME: this is weird, see issue #1458 + assert!(e1.has_layer("2")); + let history = e1.layers("2").unwrap().history(); + println!("history: {:?}", history); + assert!(e1.layers("2").unwrap().history().is_empty()); + + test_storage!(&graph, |graph| { + let e = graph.edge(1, 2).unwrap(); + // layers with non-existing layers errors + assert!(e.layers(["1", "3"]).is_err()); + // valid_layers ignores non-existing layers + assert_eq!(e.valid_layers(["1", "3"]).layer_names(), ["1"]); + assert!(e.has_layer("1")); + assert!(e.has_layer("2")); + assert!(!e.has_layer("3")); + assert!(e.valid_layers("1").has_layer("1")); + assert!(!e.valid_layers("1").has_layer("2")); + }); } - pub mod test_nodes_filters_layer_graph { + pub mod test_filters_layer_graph { use raphtory::{ db::{ api::view::StaticGraphViewOps, - graph::views::filter::model::property_filter::ops::PropertyFilterOps, - }, - prelude::AdditionOps, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - use crate::test_filters_layer_graph::{ - LayeredGraphTransformer, LayeredGraphWindowTransformer, - }; - use raphtory::{ - db::graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, - TestVariants, + graph::{ + assertions::GraphTransformer, + views::{layer_graph::LayeredGraph, window_graph::WindowedGraph}, }, - views::filter::model::PropertyFilterFactory, }, - prelude::NodeFilter, + prelude::{LayerOps, TimeOps}, }; + use std::ops::Range; - fn init_graph(graph: G) -> G { - let edges = vec![ - (6, "N1", "N2", vec![("p1", Prop::U64(2u64))], Some("layer1")), - (7, "N1", "N2", vec![("p1", Prop::U64(1u64))], Some("layer2")), - (6, "N2", "N3", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (7, "N2", "N3", vec![("p1", Prop::U64(2u64))], Some("layer2")), - (8, "N3", "N4", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (9, "N4", "N5", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (5, "N5", "N6", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (6, "N5", "N6", vec![("p1", Prop::U64(2u64))], Some("layer2")), - (5, "N6", "N7", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (6, "N6", "N7", vec![("p1", Prop::U64(1u64))], Some("layer2")), - (3, "N7", "N8", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (5, "N7", "N8", vec![("p1", Prop::U64(1u64))], Some("layer2")), - (3, "N8", "N1", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (4, "N8", "N1", vec![("p1", Prop::U64(2u64))], Some("layer2")), - ]; - - for (id, src, tgt, props, layer) in &edges { - graph - .add_edge(*id, src, tgt, props.clone(), *layer) - .unwrap(); - } + struct LayeredGraphTransformer(Vec); - let nodes = vec![ - (6, "N1", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - (7, "N1", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (6, "N2", vec![("p1", Prop::U64(1u64))], Some("water_tribe")), - (7, "N2", vec![("p1", Prop::U64(2u64))], Some("water_tribe")), - (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (5, "N5", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (6, "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (6, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (3, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (4, "N8", vec![("p1", Prop::U64(2u64))], Some("fire_nation")), - ]; - - for (id, name, props, label) in &nodes { - graph - .add_node(*id, name, props.clone(), *label, None) - .unwrap(); + impl GraphTransformer for LayeredGraphTransformer { + type Return = LayeredGraph; + fn apply(&self, graph: G) -> Self::Return { + graph.layers(self.0.clone()).unwrap() } - - graph - } - - // Layers don't have any effect on the number of nodes in a graph. - // In other words, it is as good as applying no layer filters. - #[test] - fn test_nodes_filters() { - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = NodeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2", "N5", "N8"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = NodeFilter.property("p1").lt(2u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = NodeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2", "N5", "N8"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); } - #[test] - fn test_nodes_filters_w() { - // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = NodeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + struct LayeredGraphWindowTransformer(Vec, Range); - let layers: Vec = vec!["layer2".into()]; - let filter = NodeFilter.property("p1").lt(2u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_pg_w() { - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = NodeFilter.property("p1").lt(2u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = NodeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2", "N5", "N8"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + impl GraphTransformer for LayeredGraphWindowTransformer { + type Return = WindowedGraph>; + fn apply(&self, graph: G) -> Self::Return { + graph + .layers(self.0.clone()) + .unwrap() + .window(self.1.start, self.1.end) + } } - } - mod test_edges_filters_layer_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ + pub mod test_nodes_filters_layer_graph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::property_filter::ops::PropertyFilterOps, + }, + prelude::AdditionOps, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + use crate::test::test_filters_layer_graph::{ + LayeredGraphTransformer, LayeredGraphWindowTransformer, + }; + use raphtory::{ + db::graph::{ assertions::{ - assert_filter_edges_results, assert_search_edges_results, + assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, TestVariants, }, - views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, + views::filter::model::PropertyFilterFactory, }, - }, - prelude::{AdditionOps, EdgeFilter}, - }; - use raphtory_api::core::entities::properties::prop::Prop; + prelude::NodeFilter, + }; + + fn init_graph(graph: G) -> G { + let edges = vec![ + (6, "N1", "N2", vec![("p1", Prop::U64(2u64))], Some("layer1")), + (7, "N1", "N2", vec![("p1", Prop::U64(1u64))], Some("layer2")), + (6, "N2", "N3", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (7, "N2", "N3", vec![("p1", Prop::U64(2u64))], Some("layer2")), + (8, "N3", "N4", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (9, "N4", "N5", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (5, "N5", "N6", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (6, "N5", "N6", vec![("p1", Prop::U64(2u64))], Some("layer2")), + (5, "N6", "N7", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (6, "N6", "N7", vec![("p1", Prop::U64(1u64))], Some("layer2")), + (3, "N7", "N8", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (5, "N7", "N8", vec![("p1", Prop::U64(1u64))], Some("layer2")), + (3, "N8", "N1", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (4, "N8", "N1", vec![("p1", Prop::U64(2u64))], Some("layer2")), + ]; + + for (id, src, tgt, props, layer) in &edges { + graph + .add_edge(*id, src, tgt, props.clone(), *layer) + .unwrap(); + } + + let nodes = vec![ + (6, "N1", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + (7, "N1", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (6, "N2", vec![("p1", Prop::U64(1u64))], Some("water_tribe")), + (7, "N2", vec![("p1", Prop::U64(2u64))], Some("water_tribe")), + (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (5, "N5", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (6, "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (6, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (3, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (4, "N8", vec![("p1", Prop::U64(2u64))], Some("fire_nation")), + ]; + + for (id, name, props, label) in &nodes { + graph + .add_node(*id, name, props.clone(), *label, None) + .unwrap(); + } - use crate::test_filters_layer_graph::{ - LayeredGraphTransformer, LayeredGraphWindowTransformer, - }; - - fn init_graph(graph: G) -> G { - let edges = vec![ - (6, "N1", "N2", 2u64, "layer1"), - (7, "N1", "N2", 1u64, "layer2"), - (6, "N2", "N3", 1u64, "layer1"), - (7, "N2", "N3", 2u64, "layer2"), - (8, "N3", "N4", 1u64, "layer1"), - (9, "N4", "N5", 1u64, "layer1"), - (5, "N5", "N6", 1u64, "layer1"), - (6, "N5", "N6", 2u64, "layer2"), - (5, "N6", "N7", 1u64, "layer1"), - (6, "N6", "N7", 1u64, "layer2"), - (3, "N7", "N8", 1u64, "layer1"), - (5, "N7", "N8", 1u64, "layer2"), - (3, "N8", "N1", 1u64, "layer1"), - (4, "N8", "N1", 2u64, "layer2"), - ]; - - for (ts, src, dst, p1_val, layer) in edges { graph - .add_edge(ts, src, dst, [("p1", Prop::U64(p1_val))], Some(layer)) - .unwrap(); } - graph - } - - #[test] - fn test_edges_filters() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = vec![ - "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N1", - ]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = EdgeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); + // Layers don't have any effect on the number of nodes in a graph. + // In other words, it is as good as applying no layer filters. + #[test] + fn test_nodes_filters() { + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = NodeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2", "N5", "N8"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = NodeFilter.property("p1").lt(2u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = NodeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2", "N5", "N8"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + } - let layers: Vec = vec!["layer1".into()]; - let filter = EdgeFilter.property("p1").lt(2u64); - let expected_results = vec![ - "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N1", - ]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); + #[test] + fn test_nodes_filters_w() { + // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = NodeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = NodeFilter.property("p1").lt(2u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let layers: Vec = vec!["layer2".into()]; - let filter = EdgeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); + #[test] + fn test_nodes_filters_pg_w() { + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = NodeFilter.property("p1").lt(2u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = NodeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2", "N5", "N8"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } } - #[test] - fn test_edges_filter_w() { - // Edge Property Semantics: - // 1. All property updates to an edge belong to a layer (or _default if no layer specified) - // 2. However, when asked for a value of a particular property for an edge, the latest update - // across all specified layers (or all layers if no layers specified) is returned! - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - // Edge Property Semantics: - // When filtering by specific layer, filter criteria (p1==1) and latest semantics is applicable - // only to that specific layer. - let layers: Vec = vec!["layer1".into()]; - let filter = EdgeFilter.property("p1").lt(2u64); - let expected_results = vec!["N2->N3", "N3->N4"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + mod test_edges_filters_layer_graph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::{ + assertions::{ + assert_filter_edges_results, assert_search_edges_results, + TestGraphVariants, TestVariants, + }, + views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }, + }, + }, + prelude::{AdditionOps, EdgeFilter}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + use crate::test::test_filters_layer_graph::{ + LayeredGraphTransformer, LayeredGraphWindowTransformer, + }; + + fn init_graph(graph: G) -> G { + let edges = vec![ + (6, "N1", "N2", 2u64, "layer1"), + (7, "N1", "N2", 1u64, "layer2"), + (6, "N2", "N3", 1u64, "layer1"), + (7, "N2", "N3", 2u64, "layer2"), + (8, "N3", "N4", 1u64, "layer1"), + (9, "N4", "N5", 1u64, "layer1"), + (5, "N5", "N6", 1u64, "layer1"), + (6, "N5", "N6", 2u64, "layer2"), + (5, "N6", "N7", 1u64, "layer1"), + (6, "N6", "N7", 1u64, "layer2"), + (3, "N7", "N8", 1u64, "layer1"), + (5, "N7", "N8", 1u64, "layer2"), + (3, "N8", "N1", 1u64, "layer1"), + (4, "N8", "N1", 2u64, "layer2"), + ]; + + for (ts, src, dst, p1_val, layer) in edges { + graph + .add_edge(ts, src, dst, [("p1", Prop::U64(p1_val))], Some(layer)) + .unwrap(); + } - let layers: Vec = vec!["layer2".into()]; - let filter = EdgeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + graph + } - #[test] - fn test_edges_filters_pg_w() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = EdgeFilter.property("p1").eq(1u64); - - // Why is the edge N8 -> N1 included in the results? - // The reason edge N8 -> N1 is included as part of the results because of following two semantic reasons: - // .add_edge(3, "N8", "N1", [("p1", Prop::U64(1u64))], Some("layer1")) - // .add_edge(4, "N8", "N1", [("p1", Prop::U64(2u64))], Some("layer2")) - // 1. As per layer graph semantics, every edge update belongs to a particular layer (or '_default' if no layer specified). - // This means the last_before is computed per layer and not across layers. In other words, when computing - // last_before for (N8->N1, layer1) and window(6, 9), t = 3 is the correct last before edge update timestamp and not t = 4 - // because t=4 edge update is in layer2. - // 2. Since the search is conducted across both the layers i.e., layer1 and layer2, the results are union of - // results from both layer1 and layer2. - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); + #[test] + fn test_edges_filters() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = vec![ + "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N1", + ]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = EdgeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = EdgeFilter.property("p1").lt(2u64); + let expected_results = vec![ + "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N1", + ]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = EdgeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + } - let layers: Vec = vec!["layer1".into()]; - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = vec!["N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N1"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); + #[test] + fn test_edges_filter_w() { + // Edge Property Semantics: + // 1. All property updates to an edge belong to a layer (or _default if no layer specified) + // 2. However, when asked for a value of a particular property for an edge, the latest update + // across all specified layers (or all layers if no layers specified) is returned! + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + // Edge Property Semantics: + // When filtering by specific layer, filter criteria (p1==1) and latest semantics is applicable + // only to that specific layer. + let layers: Vec = vec!["layer1".into()]; + let filter = EdgeFilter.property("p1").lt(2u64); + let expected_results = vec!["N2->N3", "N3->N4"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = EdgeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let layers: Vec = vec!["layer2".into()]; - let filter = EdgeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); + #[test] + fn test_edges_filters_pg_w() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = EdgeFilter.property("p1").eq(1u64); + + // Why is the edge N8 -> N1 included in the results? + // The reason edge N8 -> N1 is included as part of the results because of following two semantic reasons: + // .add_edge(3, "N8", "N1", [("p1", Prop::U64(1u64))], Some("layer1")) + // .add_edge(4, "N8", "N1", [("p1", Prop::U64(2u64))], Some("layer2")) + // 1. As per layer graph semantics, every edge update belongs to a particular layer (or '_default' if no layer specified). + // This means the last_before is computed per layer and not across layers. In other words, when computing + // last_before for (N8->N1, layer1) and window(6, 9), t = 3 is the correct last before edge update timestamp and not t = 4 + // because t=4 edge update is in layer2. + // 2. Since the search is conducted across both the layers i.e., layer1 and layer2, the results are union of + // results from both layer1 and layer2. + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = + vec!["N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N1"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = EdgeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + } } } } diff --git a/raphtory/tests/test_materialize.rs b/raphtory/tests/test_materialize.rs index 77a20b62b9..99ffa62d36 100644 --- a/raphtory/tests/test_materialize.rs +++ b/raphtory/tests/test_materialize.rs @@ -1,190 +1,193 @@ -use itertools::Itertools; -use proptest::{arbitrary::any, proptest}; -use raphtory::{ - db::graph::graph::{assert_graph_equal, assert_graph_equal_timestamps}, - prelude::*, - test_storage, - test_utils::{build_edge_list, build_graph_from_edge_list}, -}; -use raphtory_api::core::storage::arc_str::OptionAsStr; -use raphtory_storage::core_ops::CoreGraphOps; -use std::ops::Range; - -#[test] -fn test_materialize() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("layer1", "1")], Some("1")).unwrap(); - g.add_edge(0, 1, 2, [("layer2", "2")], Some("2")).unwrap(); - - let gm = g.materialize().unwrap(); - - assert_graph_equal(&g, &gm); - assert_eq!( - gm.nodes().name().iter_values().collect::>(), - vec!["1", "2"] - ); - - assert!(g - .layers("2") - .unwrap() - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("layer1") - .and_then(|prop| prop.latest()) - .is_none()); - - assert!(gm - .into_events() - .unwrap() - .layers("2") - .unwrap() - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("layer1") - .and_then(|prop| prop.latest()) - .is_none()); -} - -#[test] -fn test_graph_properties() { - let g = Graph::new(); - g.add_properties(1, [("test", "test")]).unwrap(); - g.add_metadata([("test_metadata", "test2")]).unwrap(); +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use proptest::{arbitrary::any, proptest}; + use raphtory::{ + db::graph::graph::{assert_graph_equal, assert_graph_equal_timestamps}, + prelude::*, + test_storage, + test_utils::{build_edge_list, build_graph_from_edge_list}, + }; + use raphtory_api::core::storage::arc_str::OptionAsStr; + use raphtory_storage::core_ops::CoreGraphOps; + use std::ops::Range; + + #[test] + fn test_materialize() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("layer1", "1")], Some("1")).unwrap(); + g.add_edge(0, 1, 2, [("layer2", "2")], Some("2")).unwrap(); - test_storage!(&g, |g| { let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); - }); -} + assert_eq!( + gm.nodes().name().iter_values().collect::>(), + vec!["1", "2"] + ); + + assert!(g + .layers("2") + .unwrap() + .edge(1, 2) + .unwrap() + .properties() + .temporal() + .get("layer1") + .and_then(|prop| prop.latest()) + .is_none()); + + assert!(gm + .into_events() + .unwrap() + .layers("2") + .unwrap() + .edge(1, 2) + .unwrap() + .properties() + .temporal() + .get("layer1") + .and_then(|prop| prop.latest()) + .is_none()); + } + + #[test] + fn test_graph_properties() { + let g = Graph::new(); + g.add_properties(1, [("test", "test")]).unwrap(); + g.add_metadata([("test_metadata", "test2")]).unwrap(); -#[test] -fn materialize_prop_test() { - proptest!(|(edges in build_edge_list(100, 100), w in any::>())| { - let g = build_graph_from_edge_list(&edges); test_storage!(&g, |g| { let gm = g.materialize().unwrap(); - assert_graph_equal_timestamps(&g, &gm); - let gw = g.window(w.start, w.end); - let gmw = gw.materialize().unwrap(); - assert_graph_equal_timestamps(&gw, &gmw); + assert_graph_equal(&g, &gm); + }); + } + + #[test] + fn materialize_prop_test() { + proptest!(|(edges in build_edge_list(100, 100), w in any::>())| { + let g = build_graph_from_edge_list(&edges); + test_storage!(&g, |g| { + let gm = g.materialize().unwrap(); + assert_graph_equal_timestamps(&g, &gm); + let gw = g.window(w.start, w.end); + let gmw = gw.materialize().unwrap(); + assert_graph_equal_timestamps(&gw, &gmw); + }); + }) + } + + #[test] + fn test_subgraph() { + let g = Graph::new(); + g.add_node(0, 1, NO_PROPS, None, None).unwrap(); + g.add_node(0, 2, NO_PROPS, None, None).unwrap(); + g.add_node(0, 3, NO_PROPS, None, None).unwrap(); + g.add_node(0, 4, NO_PROPS, None, None).unwrap(); + g.add_node(0, 5, NO_PROPS, None, None).unwrap(); + + let nodes_subgraph = g.subgraph(vec![4, 5]); + assert_eq!( + nodes_subgraph + .nodes() + .name() + .iter_values() + .collect::>(), + vec!["4", "5"] + ); + let gm = nodes_subgraph.materialize().unwrap(); + assert_graph_equal(&nodes_subgraph, &gm); + } + + #[test] + fn test_exclude_nodes() { + let g = Graph::new(); + g.add_node(0, 1, NO_PROPS, None, None).unwrap(); + g.add_node(0, 2, NO_PROPS, None, None).unwrap(); + g.add_node(0, 3, NO_PROPS, None, None).unwrap(); + g.add_node(0, 4, NO_PROPS, None, None).unwrap(); + g.add_node(0, 5, NO_PROPS, None, None).unwrap(); + + let exclude_nodes_subgraph = g.exclude_nodes(vec![4, 5]); + assert_eq!( + exclude_nodes_subgraph + .nodes() + .name() + .iter_values() + .sorted() + .collect::>(), + vec!["1", "2", "3"] + ); + let gm = exclude_nodes_subgraph.materialize().unwrap(); + assert_graph_equal(&exclude_nodes_subgraph, &gm); + } + + #[test] + fn testing_node_types() { + let graph = Graph::new(); + graph.add_node(0, "A", NO_PROPS, None, None).unwrap(); + graph.add_node(1, "B", NO_PROPS, Some("H"), None).unwrap(); + + test_storage!(&graph, |graph| { + let node_a = graph.node("A").unwrap(); + let node_b = graph.node("B").unwrap(); + let node_a_type = node_a.node_type(); + let node_a_type_str = node_a_type.as_str(); + + assert_eq!(node_a_type_str, None); + assert_eq!(node_b.node_type().as_str(), Some("H")); }); - }) -} - -#[test] -fn test_subgraph() { - let g = Graph::new(); - g.add_node(0, 1, NO_PROPS, None, None).unwrap(); - g.add_node(0, 2, NO_PROPS, None, None).unwrap(); - g.add_node(0, 3, NO_PROPS, None, None).unwrap(); - g.add_node(0, 4, NO_PROPS, None, None).unwrap(); - g.add_node(0, 5, NO_PROPS, None, None).unwrap(); - - let nodes_subgraph = g.subgraph(vec![4, 5]); - assert_eq!( - nodes_subgraph - .nodes() - .name() - .iter_values() - .collect::>(), - vec!["4", "5"] - ); - let gm = nodes_subgraph.materialize().unwrap(); - assert_graph_equal(&nodes_subgraph, &gm); -} - -#[test] -fn test_exclude_nodes() { - let g = Graph::new(); - g.add_node(0, 1, NO_PROPS, None, None).unwrap(); - g.add_node(0, 2, NO_PROPS, None, None).unwrap(); - g.add_node(0, 3, NO_PROPS, None, None).unwrap(); - g.add_node(0, 4, NO_PROPS, None, None).unwrap(); - g.add_node(0, 5, NO_PROPS, None, None).unwrap(); - - let exclude_nodes_subgraph = g.exclude_nodes(vec![4, 5]); - assert_eq!( - exclude_nodes_subgraph - .nodes() - .name() - .iter_values() - .sorted() - .collect::>(), - vec!["1", "2", "3"] - ); - let gm = exclude_nodes_subgraph.materialize().unwrap(); - assert_graph_equal(&exclude_nodes_subgraph, &gm); -} - -#[test] -fn testing_node_types() { - let graph = Graph::new(); - graph.add_node(0, "A", NO_PROPS, None, None).unwrap(); - graph.add_node(1, "B", NO_PROPS, Some("H"), None).unwrap(); - - test_storage!(&graph, |graph| { - let node_a = graph.node("A").unwrap(); - let node_b = graph.node("B").unwrap(); - let node_a_type = node_a.node_type(); - let node_a_type_str = node_a_type.as_str(); - - assert_eq!(node_a_type_str, None); - assert_eq!(node_b.node_type().as_str(), Some("H")); - }); - - // Nodes with No type can be overwritten - let node_a = graph - .add_node(1, "A", NO_PROPS, Some("TYPEA"), None) - .unwrap(); - assert_eq!(node_a.node_type().as_str(), Some("TYPEA")); - - // Check that overwriting a node type returns an error - assert!(graph - .add_node(2, "A", NO_PROPS, Some("TYPEB"), None) - .is_err()); - // Double check that the type did not actually change - assert_eq!(graph.node("A").unwrap().node_type().as_str(), Some("TYPEA")); - // Check that the update is not added to the graph - let all_node_types = graph.get_all_node_types(); - assert_eq!(all_node_types.len(), 2); -} - -#[test] -fn changing_property_type_errors() { - let g = Graph::new(); - let props_0 = [("test", Prop::U64(1))]; - let props_1 = [("test", Prop::F64(0.1))]; - g.add_properties(0, props_0.clone()).unwrap(); - assert!(g.add_properties(1, props_1.clone()).is_err()); - - g.add_node(0, 1, props_0.clone(), None, None).unwrap(); - assert!(g.add_node(1, 1, props_1.clone(), None, None).is_err()); - - g.add_edge(0, 1, 2, props_0.clone(), None).unwrap(); - assert!(g.add_edge(1, 1, 2, props_1.clone(), None).is_err()); -} -#[test] -fn test_edge_layer_properties() { - let g = Graph::new(); - g.add_edge(1, "A", "B", [("greeting", "howdy")], Some("layer 1")) - .unwrap(); - g.add_edge(2, "A", "B", [("greeting", "ola")], Some("layer 2")) - .unwrap(); - g.add_edge(2, "A", "B", [("greeting", "hello")], Some("layer 2")) - .unwrap(); - g.add_edge(3, "A", "B", [("greeting", "namaste")], Some("layer 3")) - .unwrap(); - - let edge_ab = g.edge("A", "B").unwrap(); - let props = edge_ab - .properties() - .iter() - .filter_map(|(k, v)| v.map(move |v| (k.to_string(), v.to_string()))) - .collect::>(); - assert_eq!(props, vec![("greeting".to_string(), "namaste".to_string())]); + // Nodes with No type can be overwritten + let node_a = graph + .add_node(1, "A", NO_PROPS, Some("TYPEA"), None) + .unwrap(); + assert_eq!(node_a.node_type().as_str(), Some("TYPEA")); + + // Check that overwriting a node type returns an error + assert!(graph + .add_node(2, "A", NO_PROPS, Some("TYPEB"), None) + .is_err()); + // Double check that the type did not actually change + assert_eq!(graph.node("A").unwrap().node_type().as_str(), Some("TYPEA")); + // Check that the update is not added to the graph + let all_node_types = graph.get_all_node_types(); + assert_eq!(all_node_types.len(), 2); + } + + #[test] + fn changing_property_type_errors() { + let g = Graph::new(); + let props_0 = [("test", Prop::U64(1))]; + let props_1 = [("test", Prop::F64(0.1))]; + g.add_properties(0, props_0.clone()).unwrap(); + assert!(g.add_properties(1, props_1.clone()).is_err()); + + g.add_node(0, 1, props_0.clone(), None, None).unwrap(); + assert!(g.add_node(1, 1, props_1.clone(), None, None).is_err()); + + g.add_edge(0, 1, 2, props_0.clone(), None).unwrap(); + assert!(g.add_edge(1, 1, 2, props_1.clone(), None).is_err()); + } + + #[test] + fn test_edge_layer_properties() { + let g = Graph::new(); + g.add_edge(1, "A", "B", [("greeting", "howdy")], Some("layer 1")) + .unwrap(); + g.add_edge(2, "A", "B", [("greeting", "ola")], Some("layer 2")) + .unwrap(); + g.add_edge(2, "A", "B", [("greeting", "hello")], Some("layer 2")) + .unwrap(); + g.add_edge(3, "A", "B", [("greeting", "namaste")], Some("layer 3")) + .unwrap(); + + let edge_ab = g.edge("A", "B").unwrap(); + let props = edge_ab + .properties() + .iter() + .filter_map(|(k, v)| v.map(move |v| (k.to_string(), v.to_string()))) + .collect::>(); + assert_eq!(props, vec![("greeting".to_string(), "namaste".to_string())]); + } } diff --git a/raphtory/tests/tests_node_type_filtered_subgraph.rs b/raphtory/tests/tests_node_type_filtered_subgraph.rs index 7105129b7d..8531670dea 100644 --- a/raphtory/tests/tests_node_type_filtered_subgraph.rs +++ b/raphtory/tests/tests_node_type_filtered_subgraph.rs @@ -1,680 +1,697 @@ -use proptest::{arbitrary::any, proptest}; -use raphtory::{ - db::{ - api::view::Filter, - graph::{ - graph::assert_graph_equal, - views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, - }, - }, - prelude::*, - test_utils::{build_graph, build_graph_strat, make_node_types, GraphFixture}, -}; -use raphtory_storage::mutation::addition_ops::InternalAdditionOps; -use serde_json::json; -use std::ops::Range; - -#[test] -fn test_type_filtered_subgraph() { - let graph = Graph::new(); - let edges = vec![ - (1, "A", "B", vec![("p1", 1u64)], None), - (2, "B", "C", vec![("p1", 2u64)], None), - (3, "C", "D", vec![("p1", 3u64)], None), - (4, "D", "E", vec![("p1", 4u64)], None), - ]; - - for (id, src, dst, props, layer) in &edges { - graph - .add_edge(*id, src, dst, props.clone(), *layer) - .unwrap(); - } - - let nodes = vec![ - (1, "A", vec![("p1", 1u64)], Some("water_tribe")), - (2, "B", vec![("p1", 2u64)], Some("water_tribe")), - (3, "C", vec![("p1", 1u64)], Some("fire_nation")), - (4, "D", vec![("p1", 1u64)], Some("air_nomads")), - ]; - - for (id, name, props, layer) in &nodes { - graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); - } - - let filtered_graph = graph - .subgraph_node_types(vec!["fire_nation", "air_nomads"]) - .window(1, 5); - - assert_eq!(filtered_graph.nodes(), vec!["C", "D"]); - - assert_eq!( - filtered_graph - .filter(NodeFilter.property("p1").eq(1u64)) - .unwrap() - .nodes(), - vec!["C", "D"] - ); - - assert!(filtered_graph - .filter(EdgeFilter.property("p1").eq(1u64)) - .unwrap() - .edges() - .is_empty()) -} - -#[test] -fn materialize_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), node_types in make_node_types())| { - let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); - let gm = g.materialize().unwrap(); - assert_graph_equal(&g, &gm); - }) -} - -#[test] -fn materialize_type_window_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), node_types in make_node_types())| { - let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); - let gvw = g.window(w.start, w.end); - let gmw = gvw.materialize().unwrap(); - assert_graph_equal(&gvw, &gmw); - }) -} - -#[test] -fn materialize_type_window_prop_test_failure() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{"9":{"props":{"t_props":[[1,[]]],"c_props":[]},"node_type":"one"},"8":{"props":{"t_props":[[1,[]]],"c_props":[]},"node_type":"one"}},"edges":[[[8,8,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let w = 1..2; - let node_types = ["one"]; - let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); - let gvw = g.window(w.start, w.end); - assert_eq!(gvw.node("8").unwrap().out_degree(), 0); // edge is not in the window - let gmw = gvw.materialize().unwrap(); - assert_graph_equal(&gvw, &gmw); -} - -#[test] -fn materialize_window_type_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), node_types in make_node_types())| { - let g = Graph::from(build_graph(&graph_f)); - let gvw = g.window(w.start, w.end).subgraph_node_types(node_types); - let gmw = gvw.materialize().unwrap(); - assert_graph_equal(&gvw, &gmw); - }) -} - -#[test] -fn node_removed_via_edge_removal() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.node(1).unwrap().set_node_type("test").unwrap(); - let expected = Graph::new(); - expected.resolve_layer(None).unwrap(); - assert_graph_equal(&g.subgraph_node_types(["test"]), &expected); -} - -#[test] -fn node_removed_via_edge_removal_window() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.node(0).unwrap().set_node_type("two").unwrap(); - let gw = g.window(0, 1); - let expected = Graph::new(); - expected.resolve_layer(None).unwrap(); - let sg = gw.subgraph_node_types(["_default"]); - assert!(!sg.has_node(0)); - assert!(!sg.has_node(1)); - assert_graph_equal(&sg, &expected); - assert_graph_equal(&sg, &sg.materialize().unwrap()) -} -mod test_filters_node_type_filtered_subgraph { +#[cfg(all(test, feature = "test-utils"))] +mod test { + use proptest::{arbitrary::any, proptest}; use raphtory::{ db::{ - api::{state::ops::filter::NodeTypeFilterOp, view::StaticGraphViewOps}, + api::view::Filter, graph::{ - assertions::GraphTransformer, - views::{ - filter::node_filtered_graph::NodeFilteredGraph, layer_graph::LayeredGraph, - window_graph::WindowedGraph, + graph::assert_graph_equal, + views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, }, }, }, - prelude::{GraphViewOps, LayerOps, NodeViewOps, TimeOps}, + prelude::*, + test_utils::{build_graph, build_graph_strat, make_node_types, GraphFixture}, }; + use raphtory_storage::mutation::addition_ops::InternalAdditionOps; + use serde_json::json; use std::ops::Range; - fn get_all_node_types(graph: &G) -> Vec { - graph - .nodes() - .node_type() - .into_iter() - .flat_map(|(_, node_type)| node_type) - .map(|s| s.to_string()) - .collect() - } - - struct NodeTypeGraphTransformer(Option>); - - impl GraphTransformer for NodeTypeGraphTransformer { - type Return = NodeFilteredGraph; - fn apply(&self, graph: G) -> Self::Return { - let node_types: Vec = - self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); - graph.subgraph_node_types(node_types) + #[test] + fn test_type_filtered_subgraph() { + let graph = Graph::new(); + let edges = vec![ + (1, "A", "B", vec![("p1", 1u64)], None), + (2, "B", "C", vec![("p1", 2u64)], None), + (3, "C", "D", vec![("p1", 3u64)], None), + (4, "D", "E", vec![("p1", 4u64)], None), + ]; + + for (id, src, dst, props, layer) in &edges { + graph + .add_edge(*id, src, dst, props.clone(), *layer) + .unwrap(); } - } - struct WindowedNodeTypeGraphTransformer(Option>, Range); + let nodes = vec![ + (1, "A", vec![("p1", 1u64)], Some("water_tribe")), + (2, "B", vec![("p1", 2u64)], Some("water_tribe")), + (3, "C", vec![("p1", 1u64)], Some("fire_nation")), + (4, "D", vec![("p1", 1u64)], Some("air_nomads")), + ]; - impl GraphTransformer for WindowedNodeTypeGraphTransformer { - type Return = WindowedGraph>; - fn apply(&self, graph: G) -> Self::Return { - let node_types: Vec = - self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); + for (id, name, props, layer) in &nodes { graph - .subgraph_node_types(node_types) - .window(self.1.start, self.1.end) + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); } - } - struct LayeredNodeTypeGraphTransformer(Option>, Vec); + let filtered_graph = graph + .subgraph_node_types(vec!["fire_nation", "air_nomads"]) + .window(1, 5); - impl GraphTransformer for LayeredNodeTypeGraphTransformer { - type Return = LayeredGraph>; - fn apply(&self, graph: G) -> Self::Return { - let node_types: Vec = - self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); - graph - .subgraph_node_types(node_types) - .layers(self.1.clone()) + assert_eq!(filtered_graph.nodes(), vec!["C", "D"]); + + assert_eq!( + filtered_graph + .filter(NodeFilter.property("p1").eq(1u64)) .unwrap() - } + .nodes(), + vec!["C", "D"] + ); + + assert!(filtered_graph + .filter(EdgeFilter.property("p1").eq(1u64)) + .unwrap() + .edges() + .is_empty()) } - struct LayeredWindowedNodeTypeGraphTransformer(Option>, Range, Vec); + #[test] + fn materialize_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), node_types in make_node_types())| { + let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); + let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); + }) + } - impl GraphTransformer for LayeredWindowedNodeTypeGraphTransformer { - type Return = - WindowedGraph>>; - fn apply(&self, graph: G) -> Self::Return { - let node_types: Vec = - self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); - graph - .subgraph_node_types(node_types) - .layers(self.2.clone()) - .unwrap() - .window(self.1.start, self.1.end) - } + #[test] + fn materialize_type_window_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), node_types in make_node_types())| { + let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); + let gvw = g.window(w.start, w.end); + let gmw = gvw.materialize().unwrap(); + assert_graph_equal(&gvw, &gmw); + }) + } + + #[test] + fn materialize_type_window_prop_test_failure() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{"9":{"props":{"t_props":[[1,[]]],"c_props":[]},"node_type":"one"},"8":{"props":{"t_props":[[1,[]]],"c_props":[]},"node_type":"one"}},"edges":[[[8,8,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let w = 1..2; + let node_types = ["one"]; + let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); + let gvw = g.window(w.start, w.end); + assert_eq!(gvw.node("8").unwrap().out_degree(), 0); // edge is not in the window + let gmw = gvw.materialize().unwrap(); + assert_graph_equal(&gvw, &gmw); } - mod test_nodes_filters_node_type_filtered_subgraph { + #[test] + fn materialize_window_type_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), node_types in make_node_types())| { + let g = Graph::from(build_graph(&graph_f)); + let gvw = g.window(w.start, w.end).subgraph_node_types(node_types); + let gmw = gvw.materialize().unwrap(); + assert_graph_equal(&gvw, &gmw); + }) + } + + #[test] + fn node_removed_via_edge_removal() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.node(1).unwrap().set_node_type("test").unwrap(); + let expected = Graph::new(); + expected.resolve_layer(None).unwrap(); + assert_graph_equal(&g.subgraph_node_types(["test"]), &expected); + } + + #[test] + fn node_removed_via_edge_removal_window() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.node(0).unwrap().set_node_type("two").unwrap(); + let gw = g.window(0, 1); + let expected = Graph::new(); + expected.resolve_layer(None).unwrap(); + let sg = gw.subgraph_node_types(["_default"]); + assert!(!sg.has_node(0)); + assert!(!sg.has_node(1)); + assert_graph_equal(&sg, &expected); + assert_graph_equal(&sg, &sg.materialize().unwrap()) + } + mod test_filters_node_type_filtered_subgraph { use raphtory::{ db::{ - api::view::StaticGraphViewOps, - graph::views::filter::model::property_filter::ops::PropertyFilterOps, + api::{state::ops::filter::NodeTypeFilterOp, view::StaticGraphViewOps}, + graph::{ + assertions::GraphTransformer, + views::{ + filter::node_filtered_graph::NodeFilteredGraph, layer_graph::LayeredGraph, + window_graph::WindowedGraph, + }, + }, }, - prelude::AdditionOps, + prelude::{GraphViewOps, LayerOps, NodeViewOps, TimeOps}, }; - use raphtory_api::core::entities::properties::prop::Prop; - - fn init_graph(graph: G) -> G { - let nodes = vec![ - (6, "N1", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - (7, "N1", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (6, "N2", vec![("p1", Prop::U64(1u64))], Some("water_tribe")), - (7, "N2", vec![("p1", Prop::U64(2u64))], Some("water_tribe")), - (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (5, "N5", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (6, "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (6, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (3, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (4, "N8", vec![("p1", Prop::U64(2u64))], Some("fire_nation")), - ]; - - // Add nodes to the graph - for (id, name, props, layer) in &nodes { - graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); - } + use std::ops::Range; + fn get_all_node_types(graph: &G) -> Vec { graph + .nodes() + .node_type() + .into_iter() + .flat_map(|(_, node_type)| node_type) + .map(|s| s.to_string()) + .collect() } - use crate::test_filters_node_type_filtered_subgraph::{ - NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, - }; - use raphtory::{ - db::graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, - TestVariants, - }, - views::filter::model::PropertyFilterFactory, - }, - prelude::NodeFilter, - }; - #[test] - fn test_nodes_filters() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - NodeTypeGraphTransformer(None), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - NodeTypeGraphTransformer(None), - filter, - &expected_results, - TestVariants::All, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N7"]; - assert_filter_nodes_results( - init_graph, - NodeTypeGraphTransformer(node_types.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - NodeTypeGraphTransformer(node_types), - filter, - &expected_results, - TestVariants::All, - ); + struct NodeTypeGraphTransformer(Option>); + + impl GraphTransformer for NodeTypeGraphTransformer { + type Return = NodeFilteredGraph; + fn apply(&self, graph: G) -> Self::Return { + let node_types: Vec = + self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); + graph.subgraph_node_types(node_types) + } } - #[test] - fn test_nodes_filters_w() { - // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + struct WindowedNodeTypeGraphTransformer(Option>, Range); + + impl GraphTransformer for WindowedNodeTypeGraphTransformer { + type Return = + WindowedGraph>; + fn apply(&self, graph: G) -> Self::Return { + let node_types: Vec = + self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); + graph + .subgraph_node_types(node_types) + .window(self.1.start, self.1.end) + } } - #[test] - fn test_nodes_filters_pg_w() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + struct LayeredNodeTypeGraphTransformer(Option>, Vec); + + impl GraphTransformer for LayeredNodeTypeGraphTransformer { + type Return = + LayeredGraph>; + fn apply(&self, graph: G) -> Self::Return { + let node_types: Vec = + self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); + graph + .subgraph_node_types(node_types) + .layers(self.1.clone()) + .unwrap() + } } - } - mod test_edges_filters_node_type_filtered_subgraph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::views::filter::model::property_filter::ops::PropertyFilterOps, - }, - prelude::{AdditionOps, NO_PROPS}, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - fn init_graph(graph: G) -> G { - let edges = vec![ - ( - 6, - "N1", - "N2", - vec![("p1", Prop::U64(2u64))], - Some("fire_nation"), - ), - (7, "N1", "N2", vec![("p1", Prop::U64(1u64))], None), - ( - 6, - "N2", - "N3", - vec![("p1", Prop::U64(1u64))], - Some("water_tribe"), - ), - ( - 7, - "N2", - "N3", - vec![("p1", Prop::U64(2u64))], - Some("water_tribe"), - ), - ( - 8, - "N3", - "N4", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - (9, "N4", "N5", vec![("p1", Prop::U64(1u64))], None), - ( - 5, - "N5", - "N6", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - (6, "N5", "N6", vec![("p1", Prop::U64(2u64))], None), - ( - 5, - "N6", - "N7", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - ( - 6, - "N6", - "N7", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - ( - 3, - "N7", - "N8", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - (5, "N7", "N8", vec![("p1", Prop::U64(1u64))], None), - ( - 3, - "N8", - "N1", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - ( - 4, - "N8", - "N1", - vec![("p1", Prop::U64(2u64))], - Some("water_tribe"), - ), - ]; - - for (id, src, dst, props, layer) in &edges { + struct LayeredWindowedNodeTypeGraphTransformer( + Option>, + Range, + Vec, + ); + + impl GraphTransformer for LayeredWindowedNodeTypeGraphTransformer { + type Return = + WindowedGraph>>; + fn apply(&self, graph: G) -> Self::Return { + let node_types: Vec = + self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); graph - .add_edge(*id, src, dst, props.clone(), *layer) - .unwrap(); + .subgraph_node_types(node_types) + .layers(self.2.clone()) + .unwrap() + .window(self.1.start, self.1.end) } + } + + mod test_nodes_filters_node_type_filtered_subgraph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::property_filter::ops::PropertyFilterOps, + }, + prelude::AdditionOps, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + fn init_graph(graph: G) -> G { + let nodes = vec![ + (6, "N1", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + (7, "N1", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (6, "N2", vec![("p1", Prop::U64(1u64))], Some("water_tribe")), + (7, "N2", vec![("p1", Prop::U64(2u64))], Some("water_tribe")), + (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (5, "N5", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (6, "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (6, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (3, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (4, "N8", vec![("p1", Prop::U64(2u64))], Some("fire_nation")), + ]; + + // Add nodes to the graph + for (id, name, props, layer) in &nodes { + graph + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); + } - let nodes = vec![ - (6, "N1", NO_PROPS, Some("air_nomad")), - (6, "N2", NO_PROPS, Some("water_tribe")), - (8, "N3", NO_PROPS, Some("air_nomad")), - (9, "N4", NO_PROPS, Some("air_nomad")), - (5, "N5", NO_PROPS, Some("air_nomad")), - (5, "N6", NO_PROPS, Some("fire_nation")), - (3, "N7", NO_PROPS, Some("air_nomad")), - (4, "N8", NO_PROPS, Some("fire_nation")), - ]; - - for (id, name, props, layer) in &nodes { graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); } - graph + use crate::test::test_filters_node_type_filtered_subgraph::{ + NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, + }; + use raphtory::{ + db::graph::{ + assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, + TestGraphVariants, TestVariants, + }, + views::filter::model::PropertyFilterFactory, + }, + prelude::NodeFilter, + }; + #[test] + fn test_nodes_filters() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + NodeTypeGraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + NodeTypeGraphTransformer(None), + filter, + &expected_results, + TestVariants::All, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N7"]; + assert_filter_nodes_results( + init_graph, + NodeTypeGraphTransformer(node_types.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + NodeTypeGraphTransformer(node_types), + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_filters_w() { + // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_filters_pg_w() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } } - use crate::test_filters_node_type_filtered_subgraph::{ - LayeredNodeTypeGraphTransformer, LayeredWindowedNodeTypeGraphTransformer, - NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, - }; - use raphtory::{ - db::graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestVariants, + mod test_edges_filters_node_type_filtered_subgraph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::property_filter::ops::PropertyFilterOps, }, - views::filter::model::PropertyFilterFactory, - }, - prelude::EdgeFilter, - }; + prelude::{AdditionOps, NO_PROPS}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + fn init_graph(graph: G) -> G { + let edges = vec![ + ( + 6, + "N1", + "N2", + vec![("p1", Prop::U64(2u64))], + Some("fire_nation"), + ), + (7, "N1", "N2", vec![("p1", Prop::U64(1u64))], None), + ( + 6, + "N2", + "N3", + vec![("p1", Prop::U64(1u64))], + Some("water_tribe"), + ), + ( + 7, + "N2", + "N3", + vec![("p1", Prop::U64(2u64))], + Some("water_tribe"), + ), + ( + 8, + "N3", + "N4", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + (9, "N4", "N5", vec![("p1", Prop::U64(1u64))], None), + ( + 5, + "N5", + "N6", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + (6, "N5", "N6", vec![("p1", Prop::U64(2u64))], None), + ( + 5, + "N6", + "N7", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + ( + 6, + "N6", + "N7", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + ( + 3, + "N7", + "N8", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + (5, "N7", "N8", vec![("p1", Prop::U64(1u64))], None), + ( + 3, + "N8", + "N1", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + ( + 4, + "N8", + "N1", + vec![("p1", Prop::U64(2u64))], + Some("water_tribe"), + ), + ]; + + for (id, src, dst, props, layer) in &edges { + graph + .add_edge(*id, src, dst, props.clone(), *layer) + .unwrap(); + } + + let nodes = vec![ + (6, "N1", NO_PROPS, Some("air_nomad")), + (6, "N2", NO_PROPS, Some("water_tribe")), + (8, "N3", NO_PROPS, Some("air_nomad")), + (9, "N4", NO_PROPS, Some("air_nomad")), + (5, "N5", NO_PROPS, Some("air_nomad")), + (5, "N6", NO_PROPS, Some("fire_nation")), + (3, "N7", NO_PROPS, Some("air_nomad")), + (4, "N8", NO_PROPS, Some("fire_nation")), + ]; + + for (id, name, props, layer) in &nodes { + graph + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); + } - #[test] - fn test_edges_filters() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - NodeTypeGraphTransformer(None), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - NodeTypeGraphTransformer(None), - filter, - &expected_results, - TestVariants::All, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5"]; - assert_filter_edges_results( - init_graph, - NodeTypeGraphTransformer(node_types.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - NodeTypeGraphTransformer(node_types.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let layers = vec!["fire_nation".to_string()]; - let expected_results = vec!["N3->N4"]; - assert_filter_edges_results( - init_graph, - LayeredNodeTypeGraphTransformer(node_types.clone(), layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredNodeTypeGraphTransformer(node_types.clone(), layers), - filter, - &expected_results, - TestVariants::All, - ); - } + graph + } - #[test] - fn test_edges_filters_w() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4"]; - assert_filter_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let layers = vec!["fire_nation".to_string()]; - let expected_results = vec!["N3->N4"]; - assert_filter_edges_results( - init_graph, - LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } + use crate::test::test_filters_node_type_filtered_subgraph::{ + LayeredNodeTypeGraphTransformer, LayeredWindowedNodeTypeGraphTransformer, + NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, + }; + use raphtory::{ + db::graph::{ + assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestVariants, + }, + views::filter::model::PropertyFilterFactory, + }, + prelude::EdgeFilter, + }; + + #[test] + fn test_edges_filters() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + NodeTypeGraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + NodeTypeGraphTransformer(None), + filter, + &expected_results, + TestVariants::All, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5"]; + assert_filter_edges_results( + init_graph, + NodeTypeGraphTransformer(node_types.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + NodeTypeGraphTransformer(node_types.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let layers = vec!["fire_nation".to_string()]; + let expected_results = vec!["N3->N4"]; + assert_filter_edges_results( + init_graph, + LayeredNodeTypeGraphTransformer(node_types.clone(), layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredNodeTypeGraphTransformer(node_types.clone(), layers), + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_edges_filters_pg_w() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4"]; - assert_filter_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let layers = vec!["fire_nation".to_string()]; - let expected_results = vec!["N3->N4"]; - assert_filter_edges_results( - init_graph, - LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers.clone()), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_w() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4"]; + assert_filter_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let layers = vec!["fire_nation".to_string()]; + let expected_results = vec!["N3->N4"]; + assert_filter_edges_results( + init_graph, + LayeredWindowedNodeTypeGraphTransformer( + node_types.clone(), + 6..9, + layers.clone(), + ), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_w() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4"]; + assert_filter_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let layers = vec!["fire_nation".to_string()]; + let expected_results = vec!["N3->N4"]; + assert_filter_edges_results( + init_graph, + LayeredWindowedNodeTypeGraphTransformer( + node_types.clone(), + 6..9, + layers.clone(), + ), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } } } } diff --git a/raphtory/tests/time_tests.rs b/raphtory/tests/time_tests.rs index 633967d91b..046076177a 100644 --- a/raphtory/tests/time_tests.rs +++ b/raphtory/tests/time_tests.rs @@ -1,331 +1,336 @@ -use itertools::Itertools; -use raphtory::{ - db::{ - api::{mutation::AdditionOps, view::WindowSet}, - graph::{ - graph::{assert_graph_equal, Graph}, - views::deletion_graph::PersistentGraph, +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use raphtory::{ + db::{ + api::{mutation::AdditionOps, view::WindowSet}, + graph::{ + graph::{assert_graph_equal, Graph}, + views::deletion_graph::PersistentGraph, + }, }, - }, - prelude::{DeletionOps, GraphViewOps, LayerOps, TimeOps, NO_PROPS}, - test_storage, -}; -use raphtory_api::core::{ - storage::timeindex::AsTime, - utils::time::{ParseTimeError, TryIntoTime}, -}; - -// start inclusive, end exclusive -fn graph_with_timeline(start: i64, end: i64) -> Graph { - let g = Graph::new(); - g.add_edge(start, 0, 1, NO_PROPS, None).unwrap(); - g.add_edge(end - 1, 0, 1, NO_PROPS, None).unwrap(); - g -} - -fn assert_bounds<'graph, G>(windows: WindowSet<'graph, G>, expected: &[(Option, Option)]) -where - G: GraphViewOps<'graph>, -{ - let window_bounds = windows - .map(|w| (w.start().map(|t| t.t()), w.end().map(|t| t.t()))) - .collect_vec(); - assert_eq!(window_bounds, expected) -} - -#[test] -fn snapshot() { - let graph = PersistentGraph::new(); - graph.add_edge(3, 0, 1, [("a", "a")], None).unwrap(); - graph.add_edge(4, 0, 2, [("b", "b")], None).unwrap(); - graph.delete_edge(5, 0, 1, None).unwrap(); + prelude::{DeletionOps, GraphViewOps, LayerOps, TimeOps, NO_PROPS}, + test_storage, + }; + use raphtory_api::core::{ + storage::timeindex::AsTime, + utils::time::{ParseTimeError, TryIntoTime}, + }; + + // start inclusive, end exclusive + fn graph_with_timeline(start: i64, end: i64) -> Graph { + let g = Graph::new(); + g.add_edge(start, 0, 1, NO_PROPS, None).unwrap(); + g.add_edge(end - 1, 0, 1, NO_PROPS, None).unwrap(); + g + } - for time in 2..7 { - assert_graph_equal(&graph.at(time), &graph.snapshot_at(time)); + fn assert_bounds<'graph, G>( + windows: WindowSet<'graph, G>, + expected: &[(Option, Option)], + ) where + G: GraphViewOps<'graph>, + { + let window_bounds = windows + .map(|w| (w.start().map(|t| t.t()), w.end().map(|t| t.t()))) + .collect_vec(); + assert_eq!(window_bounds, expected) } - assert_graph_equal(&graph.latest(), &graph.snapshot_latest()); - let graph = graph.event_graph(); + #[test] + fn snapshot() { + let graph = PersistentGraph::new(); + graph.add_edge(3, 0, 1, [("a", "a")], None).unwrap(); + graph.add_edge(4, 0, 2, [("b", "b")], None).unwrap(); + graph.delete_edge(5, 0, 1, None).unwrap(); - for time in 2..7 { - assert_graph_equal(&graph.before(time + 1), &graph.snapshot_at(time)); - } - assert_graph_equal(&graph, &graph.snapshot_latest()); -} + for time in 2..7 { + assert_graph_equal(&graph.at(time), &graph.snapshot_at(time)); + } + assert_graph_equal(&graph.latest(), &graph.snapshot_latest()); -#[test] -fn rolling() { - let graph = graph_with_timeline(1, 7); - test_storage!(&graph, |graph| { - let windows = graph.rolling(2, None).unwrap(); - let expected = vec![(Some(1), Some(3)), (Some(3), Some(5)), (Some(5), Some(7))]; - assert_bounds(windows, &expected); - }); - - let graph = graph_with_timeline(1, 6); - test_storage!(&graph, |graph| { - let windows = graph.rolling(3, Some(2)).unwrap(); - let expected = vec![(Some(0), Some(3)), (Some(2), Some(5)), (Some(4), Some(7))]; - assert_bounds(windows, &expected); - }); - - let graph = graph_with_timeline(0, 9); - test_storage!(&graph, |graph| { - let windows = graph.window(1, 6).rolling(3, Some(2)).unwrap(); - assert_bounds( - windows, - &[(Some(1), Some(3)), (Some(2), Some(5)), (Some(4), Some(6))], - ); - }); -} + let graph = graph.event_graph(); -#[test] -fn rolling_layered() { - let graph = Graph::new(); - graph.add_edge(1, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(5, 0, 1, NO_PROPS, Some("1")).unwrap(); - - graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); - graph.add_edge(6, 0, 1, NO_PROPS, Some("2")).unwrap(); - - test_storage!(&graph, |graph| { - let windows = graph.rolling(2, None).unwrap(); - let expected = vec![(Some(1), Some(3)), (Some(3), Some(5)), (Some(5), Some(7))]; - assert_bounds(windows, &expected); - - let windows = graph.layers("1").unwrap().rolling(2, None).unwrap(); - assert_bounds(windows, &expected); - - let windows = graph.layers("2").unwrap().rolling(2, None).unwrap(); - assert_bounds(windows, &expected); - }); - - let graph = Graph::new(); - graph.add_edge(1, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(3, 0, 1, NO_PROPS, Some("1")).unwrap(); - - graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); - graph.add_edge(5, 0, 1, NO_PROPS, Some("2")).unwrap(); - test_storage!(&graph, |graph| { - let windows = graph.rolling(3, Some(2)).unwrap(); - let expected = vec![(Some(0), Some(3)), (Some(2), Some(5)), (Some(4), Some(7))]; - assert_bounds(windows, &expected); - - let windows = graph.layers("1").unwrap().rolling(3, Some(2)).unwrap(); - assert_bounds(windows, &expected); - - let windows = graph.layers("2").unwrap().rolling(3, Some(2)).unwrap(); - assert_bounds(windows, &expected); - }); - - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(5, 0, 1, NO_PROPS, Some("1")).unwrap(); - - graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); - graph.add_edge(8, 0, 1, NO_PROPS, Some("2")).unwrap(); - test_storage!(&graph, |graph| { - let windows = graph.window(1, 6).rolling(3, Some(2)).unwrap(); - let expected = [(Some(1), Some(3)), (Some(2), Some(5)), (Some(4), Some(6))]; - assert_bounds(windows, &expected); - - let windows = graph - .layers("1") - .unwrap() - .window(1, 6) - .rolling(3, Some(2)) - .unwrap(); - assert_bounds(windows, &expected); - - let windows = graph - .layers("2") - .unwrap() - .window(1, 6) - .rolling(3, Some(2)) - .unwrap(); - assert_bounds(windows, &expected); - }); -} + for time in 2..7 { + assert_graph_equal(&graph.before(time + 1), &graph.snapshot_at(time)); + } + assert_graph_equal(&graph, &graph.snapshot_latest()); + } -#[test] -fn expanding() { - let graph = graph_with_timeline(1, 7); - test_storage!(&graph, |graph| { - let windows = graph.expanding(2).unwrap(); - let expected = vec![(None, Some(3)), (None, Some(5)), (None, Some(7))]; - assert_bounds(windows, &expected); - }); - - let graph = graph_with_timeline(1, 6); - test_storage!(&graph, |graph| { - let windows = graph.expanding(2).unwrap(); - let expected = vec![(None, Some(3)), (None, Some(5)), (None, Some(7))]; - assert_bounds(windows, &expected); - }); - - let graph = graph_with_timeline(0, 9); - test_storage!(&graph, |graph| { - let windows = graph.window(1, 6).expanding(2).unwrap(); - assert_bounds( - windows, - &[(Some(1), Some(3)), (Some(1), Some(5)), (Some(1), Some(6))], - ); - }); -} + #[test] + fn rolling() { + let graph = graph_with_timeline(1, 7); + test_storage!(&graph, |graph| { + let windows = graph.rolling(2, None).unwrap(); + let expected = vec![(Some(1), Some(3)), (Some(3), Some(5)), (Some(5), Some(7))]; + assert_bounds(windows, &expected); + }); + + let graph = graph_with_timeline(1, 6); + test_storage!(&graph, |graph| { + let windows = graph.rolling(3, Some(2)).unwrap(); + let expected = vec![(Some(0), Some(3)), (Some(2), Some(5)), (Some(4), Some(7))]; + assert_bounds(windows, &expected); + }); + + let graph = graph_with_timeline(0, 9); + test_storage!(&graph, |graph| { + let windows = graph.window(1, 6).rolling(3, Some(2)).unwrap(); + assert_bounds( + windows, + &[(Some(1), Some(3)), (Some(2), Some(5)), (Some(4), Some(6))], + ); + }); + } -#[test] -fn rolling_dates() { - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - test_storage!(&graph, |graph| { - let windows = graph.rolling("1 day", None).unwrap(); - let expected = vec![ - ( - "2020-06-06 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ( - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 - "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ]; - assert_bounds(windows, &expected); - }); - - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-08 00:00:00".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - test_storage!(&graph, |graph| { - let windows = graph.rolling("1 day", None).unwrap(); - let expected = vec![ - ( - "2020-06-06 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ( - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-07 - "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ]; - assert_bounds(windows, &expected); - }); - - // TODO: turn this back on if we bring bach epoch alignment for unwindowed graphs - // let start = "2020-06-05 23:59:59.999".into_time().unwrap(); - // let end = "2020-06-07 00:00:00.000".into_time().unwrap(); - // let g = graph_with_timeline(start, end); - // let windows = g.rolling("1 day", None).unwrap(); - // let expected = vec![ - // ( - // "2020-06-05 00:00:00".into_time().unwrap(), // entire 2020-06-06 - // "2020-06-06 00:00:00".into_time().unwrap(), - // ), - // ( - // "2020-06-06 00:00:00".into_time().unwrap(), // entire 2020-06-07 - // "2020-06-07 00:00:00".into_time().unwrap(), - // ), - // ]; - // assert_bounds(windows, expected); -} + #[test] + fn rolling_layered() { + let graph = Graph::new(); + graph.add_edge(1, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(5, 0, 1, NO_PROPS, Some("1")).unwrap(); + + graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); + graph.add_edge(6, 0, 1, NO_PROPS, Some("2")).unwrap(); + + test_storage!(&graph, |graph| { + let windows = graph.rolling(2, None).unwrap(); + let expected = vec![(Some(1), Some(3)), (Some(3), Some(5)), (Some(5), Some(7))]; + assert_bounds(windows, &expected); + + let windows = graph.layers("1").unwrap().rolling(2, None).unwrap(); + assert_bounds(windows, &expected); + + let windows = graph.layers("2").unwrap().rolling(2, None).unwrap(); + assert_bounds(windows, &expected); + }); + + let graph = Graph::new(); + graph.add_edge(1, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(3, 0, 1, NO_PROPS, Some("1")).unwrap(); + + graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); + graph.add_edge(5, 0, 1, NO_PROPS, Some("2")).unwrap(); + test_storage!(&graph, |graph| { + let windows = graph.rolling(3, Some(2)).unwrap(); + let expected = vec![(Some(0), Some(3)), (Some(2), Some(5)), (Some(4), Some(7))]; + assert_bounds(windows, &expected); + + let windows = graph.layers("1").unwrap().rolling(3, Some(2)).unwrap(); + assert_bounds(windows, &expected); + + let windows = graph.layers("2").unwrap().rolling(3, Some(2)).unwrap(); + assert_bounds(windows, &expected); + }); + + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(5, 0, 1, NO_PROPS, Some("1")).unwrap(); + + graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); + graph.add_edge(8, 0, 1, NO_PROPS, Some("2")).unwrap(); + test_storage!(&graph, |graph| { + let windows = graph.window(1, 6).rolling(3, Some(2)).unwrap(); + let expected = [(Some(1), Some(3)), (Some(2), Some(5)), (Some(4), Some(6))]; + assert_bounds(windows, &expected); + + let windows = graph + .layers("1") + .unwrap() + .window(1, 6) + .rolling(3, Some(2)) + .unwrap(); + assert_bounds(windows, &expected); + + let windows = graph + .layers("2") + .unwrap() + .window(1, 6) + .rolling(3, Some(2)) + .unwrap(); + assert_bounds(windows, &expected); + }); + } -#[test] -fn test_errors() { - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - match graph.rolling("1 day", Some("0 days")) { - Ok(_) => { - panic!("Expected error, but got Ok") - } - Err(e) => { - assert_eq!(e, ParseTimeError::ZeroSizeStep) - } + #[test] + fn expanding() { + let graph = graph_with_timeline(1, 7); + test_storage!(&graph, |graph| { + let windows = graph.expanding(2).unwrap(); + let expected = vec![(None, Some(3)), (None, Some(5)), (None, Some(7))]; + assert_bounds(windows, &expected); + }); + + let graph = graph_with_timeline(1, 6); + test_storage!(&graph, |graph| { + let windows = graph.expanding(2).unwrap(); + let expected = vec![(None, Some(3)), (None, Some(5)), (None, Some(7))]; + assert_bounds(windows, &expected); + }); + + let graph = graph_with_timeline(0, 9); + test_storage!(&graph, |graph| { + let windows = graph.window(1, 6).expanding(2).unwrap(); + assert_bounds( + windows, + &[(Some(1), Some(3)), (Some(1), Some(5)), (Some(1), Some(6))], + ); + }); } - match graph.rolling(1, Some(0)) { - Ok(_) => { - panic!("Expected error, but got Ok") - } - Err(e) => { - assert_eq!(e, ParseTimeError::ZeroSizeStep) - } + + #[test] + fn rolling_dates() { + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + test_storage!(&graph, |graph| { + let windows = graph.rolling("1 day", None).unwrap(); + let expected = vec![ + ( + "2020-06-06 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ( + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 + "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ]; + assert_bounds(windows, &expected); + }); + + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-08 00:00:00".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + test_storage!(&graph, |graph| { + let windows = graph.rolling("1 day", None).unwrap(); + let expected = vec![ + ( + "2020-06-06 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ( + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-07 + "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ]; + assert_bounds(windows, &expected); + }); + + // TODO: turn this back on if we bring bach epoch alignment for unwindowed graphs + // let start = "2020-06-05 23:59:59.999".into_time().unwrap(); + // let end = "2020-06-07 00:00:00.000".into_time().unwrap(); + // let g = graph_with_timeline(start, end); + // let windows = g.rolling("1 day", None).unwrap(); + // let expected = vec![ + // ( + // "2020-06-05 00:00:00".into_time().unwrap(), // entire 2020-06-06 + // "2020-06-06 00:00:00".into_time().unwrap(), + // ), + // ( + // "2020-06-06 00:00:00".into_time().unwrap(), // entire 2020-06-07 + // "2020-06-07 00:00:00".into_time().unwrap(), + // ), + // ]; + // assert_bounds(windows, expected); } - match graph.expanding("0 day") { - Ok(_) => { - panic!("Expected error, but got Ok") + + #[test] + fn test_errors() { + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + match graph.rolling("1 day", Some("0 days")) { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert_eq!(e, ParseTimeError::ZeroSizeStep) + } } - Err(e) => { - assert_eq!(e, ParseTimeError::ZeroSizeStep) + match graph.rolling(1, Some(0)) { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert_eq!(e, ParseTimeError::ZeroSizeStep) + } } - } - match graph.expanding(0) { - Ok(_) => { - panic!("Expected error, but got Ok") + match graph.expanding("0 day") { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert_eq!(e, ParseTimeError::ZeroSizeStep) + } } - Err(e) => { - assert_eq!(e, ParseTimeError::ZeroSizeStep) + match graph.expanding(0) { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert_eq!(e, ParseTimeError::ZeroSizeStep) + } } - } - match graph.expanding("0fead day") { - Ok(_) => { - panic!("Expected error, but got Ok") + match graph.expanding("0fead day") { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert!(matches!(e, ParseTimeError::ParseInt { .. })) + } } - Err(e) => { - assert!(matches!(e, ParseTimeError::ParseInt { .. })) - } - } - match graph.expanding("0 dadasasdy") { - Ok(_) => { - panic!("Expected error, but got Ok") + match graph.expanding("0 dadasasdy") { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert!(matches!(e, ParseTimeError::InvalidUnit { .. })) + } } - Err(e) => { - assert!(matches!(e, ParseTimeError::InvalidUnit { .. })) - } - } - assert_eq!( - graph.rolling("1 day", Some("1000 days")).unwrap().count(), - 0 - ) -} + assert_eq!( + graph.rolling("1 day", Some("1000 days")).unwrap().count(), + 0 + ) + } -#[test] -fn expanding_dates() { - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - test_storage!(&graph, |graph| { - let windows = graph.expanding("1 day").unwrap(); - let expected = vec![ - ( - None, - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ( - None, - "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ]; - assert_bounds(windows, &expected); - }); - - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-08 00:00:00".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - test_storage!(&graph, |graph| { - let windows = graph.expanding("1 day").unwrap(); - let expected = vec![ - ( - None, - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ( - None, - "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ]; - assert_bounds(windows, &expected); - }); + #[test] + fn expanding_dates() { + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + test_storage!(&graph, |graph| { + let windows = graph.expanding("1 day").unwrap(); + let expected = vec![ + ( + None, + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ( + None, + "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ]; + assert_bounds(windows, &expected); + }); + + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-08 00:00:00".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + test_storage!(&graph, |graph| { + let windows = graph.expanding("1 day").unwrap(); + let expected = vec![ + ( + None, + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ( + None, + "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ]; + assert_bounds(windows, &expected); + }); + } } diff --git a/raphtory/tests/valid_graph.rs b/raphtory/tests/valid_graph.rs index d3dd971db2..d34955c081 100644 --- a/raphtory/tests/valid_graph.rs +++ b/raphtory/tests/valid_graph.rs @@ -1,378 +1,382 @@ -use itertools::Itertools; -use proptest::{arbitrary::any, proptest}; -use raphtory::{ - db::graph::{ - graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, - views::deletion_graph::PersistentGraph, - }, - errors::GraphError, - prelude::*, - test_utils::{build_graph, build_graph_strat}, -}; -use raphtory_api::core::storage::timeindex::AsTime; -use raphtory_storage::mutation::addition_ops::InternalAdditionOps; -use std::ops::Range; - -#[test] -fn test_valid_graph_persistent() -> Result<(), GraphError> { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None)?; - g.delete_edge(10, 0, 1, None)?; - - assert_eq!( - g.window(0, 2).valid().edges().id().collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); - assert!(g.valid().edges().is_empty()); - Ok(()) -} - -#[test] -fn test_valid_graph_events() -> Result<(), GraphError> { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None)?; - g.delete_edge(10, 0, 1, None)?; - - assert_eq!( - g.window(0, 2).valid().edges().id().collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); - assert!(g.window(1, 20).valid().edges().is_empty()); // only deletion event in window, not valid - assert_eq!( - g.valid().edges().id().collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); - Ok(()) -} - -#[test] -fn materialize_prop_test_persistent() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { - let g = PersistentGraph::from(build_graph(&graph_f)).valid(); - let gm = g.materialize().unwrap(); - assert_graph_equal(&g, &gm); - }) -} - -#[test] -fn test_explode_layers() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, Some("a")).unwrap(); - g.delete_edge(0, 0, 1, Some("b")).unwrap(); - let gv = g.valid(); - let edge = gv.edge(0, 1).unwrap(); - let exploded = edge.explode_layers(); - let layers = exploded - .layer_name() - .collect::, _>>() - .unwrap(); - assert_eq!(layers, ["a"]); -} - -#[test] -fn materialize_prop_test_events() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { - let g = Graph::from(build_graph(&graph_f)).valid(); - let gm = g.materialize().unwrap(); +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use proptest::{arbitrary::any, proptest}; + use raphtory::{ + db::graph::{ + graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, + views::deletion_graph::PersistentGraph, + }, + errors::GraphError, + prelude::*, + test_utils::{build_graph, build_graph_strat}, + }; + use raphtory_api::core::storage::timeindex::AsTime; + use raphtory_storage::mutation::addition_ops::InternalAdditionOps; + use std::ops::Range; + + #[test] + fn test_valid_graph_persistent() -> Result<(), GraphError> { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None)?; + g.delete_edge(10, 0, 1, None)?; + + assert_eq!( + g.window(0, 2).valid().edges().id().collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); + assert!(g.valid().edges().is_empty()); + Ok(()) + } + + #[test] + fn test_valid_graph_events() -> Result<(), GraphError> { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None)?; + g.delete_edge(10, 0, 1, None)?; + + assert_eq!( + g.window(0, 2).valid().edges().id().collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); + assert!(g.window(1, 20).valid().edges().is_empty()); // only deletion event in window, not valid + assert_eq!( + g.valid().edges().id().collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); + Ok(()) + } + + #[test] + fn materialize_prop_test_persistent() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { + let g = PersistentGraph::from(build_graph(&graph_f)).valid(); + let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); + }) + } + + #[test] + fn test_explode_layers() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, Some("a")).unwrap(); + g.delete_edge(0, 0, 1, Some("b")).unwrap(); + let gv = g.valid(); + let edge = gv.edge(0, 1).unwrap(); + let exploded = edge.explode_layers(); + let layers = exploded + .layer_name() + .collect::, _>>() + .unwrap(); + assert_eq!(layers, ["a"]); + } + + #[test] + fn materialize_prop_test_events() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { + let g = Graph::from(build_graph(&graph_f)).valid(); + let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); + }) + } + + #[test] + fn test_materialize_self_loop() { + let g = Graph::new(); + g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); + let gm = g.valid().materialize().unwrap(); assert_graph_equal(&g, &gm); - }) -} - -#[test] -fn test_materialize_self_loop() { - let g = Graph::new(); - g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); - let gm = g.valid().materialize().unwrap(); - assert_graph_equal(&g, &gm); -} - -#[test] -fn test_single_deleted_edge_events() { - let g = Graph::new(); - g.delete_edge(0, 0, 0, Some("a")).unwrap(); - let gv = g.valid(); - assert_eq!(gv.count_nodes(), 0); - assert_eq!(gv.count_edges(), 0); - assert_eq!(gv.count_temporal_edges(), 0); - - assert_eq!(gv.valid_layers("a").count_nodes(), 0); - - let expected = Graph::new(); - expected.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gv, &expected); - let gvm = gv.materialize().unwrap(); - assert_graph_equal(&gv, &gvm); -} - -#[test] -fn test_single_deleted_edge_persistent() { - let g = PersistentGraph::new(); - g.delete_edge(0, 0, 0, Some("a")).unwrap(); - let gv = g.valid(); - assert_eq!(gv.count_nodes(), 0); - assert_eq!(gv.count_edges(), 0); - assert_eq!(gv.count_temporal_edges(), 0); - - let expected = PersistentGraph::new(); - expected.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gv, &expected); - let gvm = gv.materialize().unwrap(); - assert_graph_equal(&gv, &gvm); -} - -#[test] -fn materialize_valid_window_persistent_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = PersistentGraph::from(build_graph(&graph_f)); - let gvw = g.valid().window(w.start, w.end); - let gmw = gvw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gvw, &gmw); - }) -} - -#[test] -fn test_deletions_in_window_but_edge_valid() { - let g = PersistentGraph::new(); - g.delete_edge(0, 0, 0, None).unwrap(); - g.delete_edge(0, 0, 1, None).unwrap(); - g.add_edge(5, 0, 1, NO_PROPS, None).unwrap(); - let gvw = g.valid().window(-1, 1); - assert_eq!(gvw.node(0).unwrap().out_degree(), 1); -} - -#[test] -fn materialize_valid_window_events_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = Graph::from(build_graph(&graph_f)); - let gvw = g.valid().window(w.start, w.end); - let gmw = gvw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gvw, &gmw); - }) -} - -#[test] -fn materialize_window_valid_persistent_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = PersistentGraph::from(build_graph(&graph_f)); - let gvw = g.window(w.start, w.end).valid(); - let gmw = gvw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gvw, &gmw); - }) -} - -#[test] -fn materialize_window_valid_events_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = Graph::from(build_graph(&graph_f)); - let gvw = g.window(w.start, w.end).valid(); - let gmw = gvw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gvw, &gmw); - }) -} - -#[test] -fn broken_earliest_time() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(1, 0, 1, None).unwrap(); - let gv = g.valid(); - let expected = PersistentGraph::new(); - expected.resolve_layer(None).unwrap(); - assert_graph_equal(&gv, &expected); - let gm = gv.materialize().unwrap(); - assert_graph_equal(&gv, &gm); -} - -#[test] -fn broken_earliest_time2() { - let g = PersistentGraph::new(); - - g.add_edge(10, 0, 0, NO_PROPS, None).unwrap(); - g.delete_edge(0, 0, 0, None).unwrap(); - - let w = 1..11; - - let gv = g.valid(); - assert_eq!(gv.node(0).unwrap().earliest_time().map(|t| t.t()), Some(0)); - - let gvw = gv.window(w.start, w.end); - assert_eq!( - gvw.node(0).unwrap().earliest_time().map(|t| t.t()), - Some(10) - ); - - assert_eq!(gvw.node(0).unwrap().history(), [10]); - - let gvwm = gvw.materialize().unwrap(); - assert_eq!( - gvwm.node(0).unwrap().earliest_time().map(|t| t.t()), - Some(10) - ); -} - -#[test] -fn broken_earliest_time3() { - let g = PersistentGraph::new(); - g.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); - g.delete_edge(100, 0, 0, None).unwrap(); - let gvw = g.valid().window(2, 20); - assert_eq!( - gvw.node(0).unwrap().earliest_time().map(|t| t.t()), - Some(10) - ); - let gvwm = gvw.materialize().unwrap(); - println!("{:?}", gvwm); - assert_eq!( - gvwm.node(0).unwrap().earliest_time().map(|t| t.t()), - Some(10) - ); -} - -#[test] -fn missing_temporal_edge() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(7658643179498972033, 0, 0, NO_PROPS, None) - .unwrap(); - g.add_edge(781965068308597440, 0, 0, NO_PROPS, Some("b")) - .unwrap(); - let gv = g.valid(); - assert_graph_equal(&gv, &g); - let gvm = gv.materialize().unwrap(); - println!("{:?}", gvm); - assert_graph_equal(&gv, &gvm); -} - -#[test] -fn wrong_temporal_edge_count() { - let g = PersistentGraph::new(); - g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.add_edge(1, 0, 1, NO_PROPS, None).unwrap(); - g.add_edge(2, 1, 0, NO_PROPS, None).unwrap(); - g.add_edge(3, 1, 0, NO_PROPS, Some("b")).unwrap(); - let gw = g.valid().window(0, 9); - let gwm = gw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gw, &gwm); // PersistentGraph ignores the earliest time's event id -} - -#[test] -fn mismatched_edge_properties() { - let g = PersistentGraph::new(); - g.add_edge(2, 1, 0, [("test", 1)], Some("b")).unwrap(); - g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); - g.add_edge(0, 0, 1, NO_PROPS, None) - .unwrap() - .add_metadata([("const_test", 2)], None) - .unwrap(); - - let gw = g.valid().window(-1, 1); - assert_eq!( - gw.edge(0, 1).unwrap().metadata().get("const_test").unwrap(), - Prop::map([("_default", 2)]) - ); - assert_eq!( - gw.edge(0, 1) + } + + #[test] + fn test_single_deleted_edge_events() { + let g = Graph::new(); + g.delete_edge(0, 0, 0, Some("a")).unwrap(); + let gv = g.valid(); + assert_eq!(gv.count_nodes(), 0); + assert_eq!(gv.count_edges(), 0); + assert_eq!(gv.count_temporal_edges(), 0); + + assert_eq!(gv.valid_layers("a").count_nodes(), 0); + + let expected = Graph::new(); + expected.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gv, &expected); + let gvm = gv.materialize().unwrap(); + assert_graph_equal(&gv, &gvm); + } + + #[test] + fn test_single_deleted_edge_persistent() { + let g = PersistentGraph::new(); + g.delete_edge(0, 0, 0, Some("a")).unwrap(); + let gv = g.valid(); + assert_eq!(gv.count_nodes(), 0); + assert_eq!(gv.count_edges(), 0); + assert_eq!(gv.count_temporal_edges(), 0); + + let expected = PersistentGraph::new(); + expected.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gv, &expected); + let gvm = gv.materialize().unwrap(); + assert_graph_equal(&gv, &gvm); + } + + #[test] + fn materialize_valid_window_persistent_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = PersistentGraph::from(build_graph(&graph_f)); + let gvw = g.valid().window(w.start, w.end); + let gmw = gvw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gvw, &gmw); + }) + } + + #[test] + fn test_deletions_in_window_but_edge_valid() { + let g = PersistentGraph::new(); + g.delete_edge(0, 0, 0, None).unwrap(); + g.delete_edge(0, 0, 1, None).unwrap(); + g.add_edge(5, 0, 1, NO_PROPS, None).unwrap(); + let gvw = g.valid().window(-1, 1); + assert_eq!(gvw.node(0).unwrap().out_degree(), 1); + } + + #[test] + fn materialize_valid_window_events_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = Graph::from(build_graph(&graph_f)); + let gvw = g.valid().window(w.start, w.end); + let gmw = gvw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gvw, &gmw); + }) + } + + #[test] + fn materialize_window_valid_persistent_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = PersistentGraph::from(build_graph(&graph_f)); + let gvw = g.window(w.start, w.end).valid(); + let gmw = gvw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gvw, &gmw); + }) + } + + #[test] + fn materialize_window_valid_events_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = Graph::from(build_graph(&graph_f)); + let gvw = g.window(w.start, w.end).valid(); + let gmw = gvw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gvw, &gmw); + }) + } + + #[test] + fn broken_earliest_time() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(1, 0, 1, None).unwrap(); + let gv = g.valid(); + let expected = PersistentGraph::new(); + expected.resolve_layer(None).unwrap(); + assert_graph_equal(&gv, &expected); + let gm = gv.materialize().unwrap(); + assert_graph_equal(&gv, &gm); + } + + #[test] + fn broken_earliest_time2() { + let g = PersistentGraph::new(); + + g.add_edge(10, 0, 0, NO_PROPS, None).unwrap(); + g.delete_edge(0, 0, 0, None).unwrap(); + + let w = 1..11; + + let gv = g.valid(); + assert_eq!(gv.node(0).unwrap().earliest_time().map(|t| t.t()), Some(0)); + + let gvw = gv.window(w.start, w.end); + assert_eq!( + gvw.node(0).unwrap().earliest_time().map(|t| t.t()), + Some(10) + ); + + assert_eq!(gvw.node(0).unwrap().history(), [10]); + + let gvwm = gvw.materialize().unwrap(); + assert_eq!( + gvwm.node(0).unwrap().earliest_time().map(|t| t.t()), + Some(10) + ); + } + + #[test] + fn broken_earliest_time3() { + let g = PersistentGraph::new(); + g.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); + g.delete_edge(100, 0, 0, None).unwrap(); + let gvw = g.valid().window(2, 20); + assert_eq!( + gvw.node(0).unwrap().earliest_time().map(|t| t.t()), + Some(10) + ); + let gvwm = gvw.materialize().unwrap(); + println!("{:?}", gvwm); + assert_eq!( + gvwm.node(0).unwrap().earliest_time().map(|t| t.t()), + Some(10) + ); + } + + #[test] + fn missing_temporal_edge() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(7658643179498972033, 0, 0, NO_PROPS, None) + .unwrap(); + g.add_edge(781965068308597440, 0, 0, NO_PROPS, Some("b")) + .unwrap(); + let gv = g.valid(); + assert_graph_equal(&gv, &g); + let gvm = gv.materialize().unwrap(); + println!("{:?}", gvm); + assert_graph_equal(&gv, &gvm); + } + + #[test] + fn wrong_temporal_edge_count() { + let g = PersistentGraph::new(); + g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.add_edge(1, 0, 1, NO_PROPS, None).unwrap(); + g.add_edge(2, 1, 0, NO_PROPS, None).unwrap(); + g.add_edge(3, 1, 0, NO_PROPS, Some("b")).unwrap(); + let gw = g.valid().window(0, 9); + let gwm = gw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gw, &gwm); // PersistentGraph ignores the earliest time's event id + } + + #[test] + fn mismatched_edge_properties() { + let g = PersistentGraph::new(); + g.add_edge(2, 1, 0, [("test", 1)], Some("b")).unwrap(); + g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); + g.add_edge(0, 0, 1, NO_PROPS, None) .unwrap() - .default_layer() - .metadata() - .get("const_test") - .unwrap(), - 2.into() - ); - assert_graph_equal(&gw, &gw.materialize().unwrap()); -} - -#[test] -fn node_earliest_time() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.add_edge(0, 0, 2, NO_PROPS, None).unwrap(); - g.add_edge(2, 0, 2, NO_PROPS, None).unwrap(); - g.delete_edge(-10, 0, 1, None).unwrap(); - - let gv = g.valid().window(-1, 10); - let gvm = gv.materialize().unwrap(); - assert_graph_equal(&gv, &gvm); - assert_eq!(gv.node(0).unwrap().earliest_time().map(|t| t.t()), Some(0)); -} - -#[test] -fn broken_degree() { - let g = PersistentGraph::new(); - g.add_edge(0, 5, 4, NO_PROPS, None).unwrap(); - g.add_edge(0, 4, 9, NO_PROPS, None).unwrap(); - g.add_edge(0, 4, 6, NO_PROPS, None).unwrap(); - g.add_edge(0, 4, 6, NO_PROPS, Some("b")).unwrap(); - g.add_edge(1, 4, 9, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 5, 4, NO_PROPS, Some("a")).unwrap(); - g.delete_edge(10, 5, 4, None).unwrap(); - - let gv = g.valid().window(0, 20); - assert!(!gv.default_layer().has_edge(5, 4)); - assert_eq!(gv.edge(5, 4).unwrap().latest_time().map(|t| t.t()), Some(2)); - assert_eq!(gv.earliest_time().map(|t| t.t()), Some(0)); - assert_eq!(gv.latest_time().map(|t| t.t()), Some(2)); - assert_eq!(gv.node(6).unwrap().latest_time().map(|t| t.t()), Some(0)); - let expected = PersistentGraph::new(); - expected.add_edge((0, 1), 4, 9, NO_PROPS, None).unwrap(); - expected.add_edge((0, 2), 4, 6, NO_PROPS, None).unwrap(); - expected - .add_edge((0, 3), 4, 6, NO_PROPS, Some("b")) - .unwrap(); - expected - .add_edge((1, 4), 4, 9, NO_PROPS, Some("a")) - .unwrap(); - expected - .add_edge((2, 5), 5, 4, NO_PROPS, Some("a")) - .unwrap(); - - assert_persistent_materialize_graph_equal(&gv, &expected); // PersistentGraph ignores the earliest time's event id - - let n4 = gv.node(4).unwrap(); - assert_eq!(n4.out_degree(), 2); - assert_eq!(n4.in_degree(), 1); - - assert_persistent_materialize_graph_equal(&gv, &gv.materialize().unwrap()); // PersistentGraph ignores the earliest time's event id -} - -#[test] -fn weird_empty_graph() { - let g = PersistentGraph::new(); - g.add_edge(10, 2, 1, NO_PROPS, Some("a")).unwrap(); - g.delete_edge(5, 2, 1, None).unwrap(); - g.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); - let gvw = g.valid().window(0, 5); - assert_eq!(gvw.count_nodes(), 0); - let expected = PersistentGraph::new(); - expected.resolve_layer(None).unwrap(); - expected.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gvw, &expected); - let gvwm = gvw.materialize().unwrap(); - assert_graph_equal(&gvw, &gvwm); -} - -#[test] -fn mismatched_node_earliest_time_again() { - let g = PersistentGraph::new(); - g.add_node(-2925244660385668056, 1, NO_PROPS, None, None) - .unwrap(); - g.add_edge(1116793271088085151, 2, 1, NO_PROPS, Some("a")) - .unwrap(); - g.add_edge(0, 9, 1, NO_PROPS, None).unwrap(); - g.delete_edge(7891470373966857988, 9, 1, None).unwrap(); - g.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); - - let gv = g.window(-2925244660385668055, 7060945172792084486).valid(); - assert_persistent_materialize_graph_equal(&gv, &gv.materialize().unwrap()); -} - -#[test] -fn test_single_edge() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - let gwv = g.window(10, 11).valid(); - let gm = gwv.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gwv, &gm); + .add_metadata([("const_test", 2)], None) + .unwrap(); + + let gw = g.valid().window(-1, 1); + assert_eq!( + gw.edge(0, 1).unwrap().metadata().get("const_test").unwrap(), + Prop::map([("_default", 2)]) + ); + assert_eq!( + gw.edge(0, 1) + .unwrap() + .default_layer() + .metadata() + .get("const_test") + .unwrap(), + 2.into() + ); + assert_graph_equal(&gw, &gw.materialize().unwrap()); + } + + #[test] + fn node_earliest_time() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.add_edge(0, 0, 2, NO_PROPS, None).unwrap(); + g.add_edge(2, 0, 2, NO_PROPS, None).unwrap(); + g.delete_edge(-10, 0, 1, None).unwrap(); + + let gv = g.valid().window(-1, 10); + let gvm = gv.materialize().unwrap(); + assert_graph_equal(&gv, &gvm); + assert_eq!(gv.node(0).unwrap().earliest_time().map(|t| t.t()), Some(0)); + } + + #[test] + fn broken_degree() { + let g = PersistentGraph::new(); + g.add_edge(0, 5, 4, NO_PROPS, None).unwrap(); + g.add_edge(0, 4, 9, NO_PROPS, None).unwrap(); + g.add_edge(0, 4, 6, NO_PROPS, None).unwrap(); + g.add_edge(0, 4, 6, NO_PROPS, Some("b")).unwrap(); + g.add_edge(1, 4, 9, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 5, 4, NO_PROPS, Some("a")).unwrap(); + g.delete_edge(10, 5, 4, None).unwrap(); + + let gv = g.valid().window(0, 20); + assert!(!gv.default_layer().has_edge(5, 4)); + assert_eq!(gv.edge(5, 4).unwrap().latest_time().map(|t| t.t()), Some(2)); + assert_eq!(gv.earliest_time().map(|t| t.t()), Some(0)); + assert_eq!(gv.latest_time().map(|t| t.t()), Some(2)); + assert_eq!(gv.node(6).unwrap().latest_time().map(|t| t.t()), Some(0)); + let expected = PersistentGraph::new(); + expected.add_edge((0, 1), 4, 9, NO_PROPS, None).unwrap(); + expected.add_edge((0, 2), 4, 6, NO_PROPS, None).unwrap(); + expected + .add_edge((0, 3), 4, 6, NO_PROPS, Some("b")) + .unwrap(); + expected + .add_edge((1, 4), 4, 9, NO_PROPS, Some("a")) + .unwrap(); + expected + .add_edge((2, 5), 5, 4, NO_PROPS, Some("a")) + .unwrap(); + + assert_persistent_materialize_graph_equal(&gv, &expected); // PersistentGraph ignores the earliest time's event id + + let n4 = gv.node(4).unwrap(); + assert_eq!(n4.out_degree(), 2); + assert_eq!(n4.in_degree(), 1); + + assert_persistent_materialize_graph_equal(&gv, &gv.materialize().unwrap()); + // PersistentGraph ignores the earliest time's event id + } + + #[test] + fn weird_empty_graph() { + let g = PersistentGraph::new(); + g.add_edge(10, 2, 1, NO_PROPS, Some("a")).unwrap(); + g.delete_edge(5, 2, 1, None).unwrap(); + g.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); + let gvw = g.valid().window(0, 5); + assert_eq!(gvw.count_nodes(), 0); + let expected = PersistentGraph::new(); + expected.resolve_layer(None).unwrap(); + expected.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gvw, &expected); + let gvwm = gvw.materialize().unwrap(); + assert_graph_equal(&gvw, &gvwm); + } + + #[test] + fn mismatched_node_earliest_time_again() { + let g = PersistentGraph::new(); + g.add_node(-2925244660385668056, 1, NO_PROPS, None, None) + .unwrap(); + g.add_edge(1116793271088085151, 2, 1, NO_PROPS, Some("a")) + .unwrap(); + g.add_edge(0, 9, 1, NO_PROPS, None).unwrap(); + g.delete_edge(7891470373966857988, 9, 1, None).unwrap(); + g.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); + + let gv = g.window(-2925244660385668055, 7060945172792084486).valid(); + assert_persistent_materialize_graph_equal(&gv, &gv.materialize().unwrap()); + } + + #[test] + fn test_single_edge() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + let gwv = g.window(10, 11).valid(); + let gm = gwv.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gwv, &gm); + } } diff --git a/raphtory/tests/views_test.rs b/raphtory/tests/views_test.rs index fdf70f6a0f..3aed3a64e5 100644 --- a/raphtory/tests/views_test.rs +++ b/raphtory/tests/views_test.rs @@ -1,4591 +1,4607 @@ -use itertools::Itertools; -use proptest::{prop_assert, prop_assert_eq, prop_assume, proptest}; -use rand::{prelude::*, rng}; -use raphtory::{ - algorithms::centrality::degree_centrality::degree_centrality, - db::graph::{graph::assert_graph_equal, views::window_graph::WindowedGraph}, - prelude::*, - test_storage, - test_utils::test_graph, -}; -use raphtory_api::core::{ - entities::GID, - storage::timeindex::AsTime, - utils::{logging::global_info_logger, time::IntoTime}, -}; -use rayon::prelude::*; -use std::ops::Range; -use tracing::{error, info}; - -#[test] -fn test_non_restricted_window() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - for n in g.window(0, 1).nodes() { - assert!(g.has_node(n)); - } +#[cfg(all(test, feature = "test-utils"))] +mod test { + use itertools::Itertools; + use proptest::{prop_assert, prop_assert_eq, prop_assume, proptest}; + use rand::{prelude::*, rng}; + use raphtory::{ + algorithms::centrality::degree_centrality::degree_centrality, + db::graph::{graph::assert_graph_equal, views::window_graph::WindowedGraph}, + prelude::*, + test_storage, + test_utils::test_graph, + }; + use raphtory_api::core::{ + entities::GID, + storage::timeindex::AsTime, + utils::{logging::global_info_logger, time::IntoTime}, + }; + use rayon::prelude::*; + use std::ops::Range; + use tracing::{error, info}; + + #[test] + fn test_non_restricted_window() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - assert_graph_equal(&g.window(0, 1), &g) -} + for n in g.window(0, 1).nodes() { + assert!(g.has_node(n)); + } -#[test] -fn windowed_graph_nodes_degree() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + assert_graph_equal(&g.window(0, 1), &g) } - test_storage!(&graph, |graph| { - let wg = graph.window(-1, 1); - let actual = wg - .nodes() - .iter() - .map(|v| (v.id(), v.degree())) - .collect::>(); + #[test] + fn windowed_graph_nodes_degree() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let wg = graph.window(-1, 1); - let expected = vec![(GID::U64(1), 2), (GID::U64(2), 1)]; + let actual = wg + .nodes() + .iter() + .map(|v| (v.id(), v.degree())) + .collect::>(); - assert_eq!(actual, expected); - }); -} + let expected = vec![(GID::U64(1), 2), (GID::U64(2), 1)]; -#[test] -fn windowed_graph_edge() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in vs { - graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + assert_eq!(actual, expected); + }); } - test_storage!(&graph, |graph| { - let wg = graph.window(i64::MIN, i64::MAX); - assert_eq!(wg.edge(1, 3).unwrap().src().id(), GID::U64(1)); - assert_eq!(wg.edge(1, 3).unwrap().dst().id(), GID::U64(3)); - }); -} -#[test] -fn windowed_graph_node_edges() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + #[test] + fn windowed_graph_edge() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + for (t, src, dst) in vs { + graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let wg = graph.window(i64::MIN, i64::MAX); + assert_eq!(wg.edge(1, 3).unwrap().src().id(), GID::U64(1)); + assert_eq!(wg.edge(1, 3).unwrap().dst().id(), GID::U64(3)); + }); } - test_storage!(&graph, |graph| { - let wg = graph.window(-1, 1); - assert_eq!(wg.node(1).unwrap().id(), GID::U64(1)); - }); -} + #[test] + fn windowed_graph_node_edges() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let wg = graph.window(-1, 1); -#[test] -fn graph_has_node_check_fail() { - let vs: Vec<(i64, u64)> = vec![ - (1, 0), - (-100, 262), - // (327226439, 108748364996394682), - (1, 9135428456135679950), - // (0, 1), - // (2, 2), - ]; - let graph = Graph::new(); - - for (t, v) in &vs { - graph.add_node(*t, *v, NO_PROPS, None, None).unwrap(); + assert_eq!(wg.node(1).unwrap().id(), GID::U64(1)); + }); } - // FIXME: Issue #46: arrow_test(&graph, test) - test_graph(&graph, |graph| { - let wg = graph.window(1, 2); - assert!(!wg.has_node(262)) - }); -} + #[test] + fn graph_has_node_check_fail() { + let vs: Vec<(i64, u64)> = vec![ + (1, 0), + (-100, 262), + // (327226439, 108748364996394682), + (1, 9135428456135679950), + // (0, 1), + // (2, 2), + ]; + let graph = Graph::new(); -#[test] -fn windowed_graph_has_node() { - proptest!(|(mut vs: Vec<(i64, u64)>)| { - global_info_logger(); - prop_assume!(!vs.is_empty()); + for (t, v) in &vs { + graph.add_node(*t, *v, NO_PROPS, None, None).unwrap(); + } - vs.sort_by_key(|v| v.1); // Sorted by node - vs.dedup_by_key(|v| v.1); // Have each node only once to avoid headaches - vs.sort_by_key(|v| v.0); // Sorted by time + // FIXME: Issue #46: arrow_test(&graph, test) + test_graph(&graph, |graph| { + let wg = graph.window(1, 2); + assert!(!wg.has_node(262)) + }); + } - let rand_start_index = rng().random_range(0..vs.len()); - let rand_end_index = rng().random_range(rand_start_index..vs.len()); + #[test] + fn windowed_graph_has_node() { + proptest!(|(mut vs: Vec<(i64, u64)>)| { + global_info_logger(); + prop_assume!(!vs.is_empty()); - let g = Graph::new(); + vs.sort_by_key(|v| v.1); // Sorted by node + vs.dedup_by_key(|v| v.1); // Have each node only once to avoid headaches + vs.sort_by_key(|v| v.0); // Sorted by time - for (t, v) in &vs { - g.add_node(*t, *v, NO_PROPS, None, None) - .map_err(|err| error!("{:?}", err)) - .ok(); - } + let rand_start_index = rng().random_range(0..vs.len()); + let rand_end_index = rng().random_range(rand_start_index..vs.len()); - let start = vs.get(rand_start_index).expect("start index in range").0; - let end = vs.get(rand_end_index).expect("end index in range").0; + let g = Graph::new(); - let wg = g.window(start, end); + for (t, v) in &vs { + g.add_node(*t, *v, NO_PROPS, None, None) + .map_err(|err| error!("{:?}", err)) + .ok(); + } - let rand_test_index: usize = rng().random_range(0..vs.len()); + let start = vs.get(rand_start_index).expect("start index in range").0; + let end = vs.get(rand_end_index).expect("end index in range").0; - let (i, v) = vs.get(rand_test_index).expect("test index in range"); - if (start..end).contains(i) { - prop_assert!(wg.has_node(*v), "Node {:?} was not in window {:?}", (i, v), start..end); - } else { - prop_assert!(!wg.has_node(*v), "Node {:?} was in window {:?}", (i, v), start..end); - } - }); -} + let wg = g.window(start, end); -#[test] -fn windowed_graph_has_edge() { - proptest!(|(mut edges: Vec<(i64, (u64, u64))>)| { - prop_assume!(!edges.is_empty()); + let rand_test_index: usize = rng().random_range(0..vs.len()); - edges.sort_by_key(|e| e.1); // Sorted by edge - edges.dedup_by_key(|e| e.1); // Have each edge only once to avoid headaches - edges.sort_by_key(|e| e.0); // Sorted by time + let (i, v) = vs.get(rand_test_index).expect("test index in range"); + if (start..end).contains(i) { + prop_assert!(wg.has_node(*v), "Node {:?} was not in window {:?}", (i, v), start..end); + } else { + prop_assert!(!wg.has_node(*v), "Node {:?} was in window {:?}", (i, v), start..end); + } + }); + } - let rand_start_index = rng().random_range(0..edges.len()); - let rand_end_index = rng().random_range(rand_start_index..edges.len()); + #[test] + fn windowed_graph_has_edge() { + proptest!(|(mut edges: Vec<(i64, (u64, u64))>)| { + prop_assume!(!edges.is_empty()); - let g = Graph::new(); + edges.sort_by_key(|e| e.1); // Sorted by edge + edges.dedup_by_key(|e| e.1); // Have each edge only once to avoid headaches + edges.sort_by_key(|e| e.0); // Sorted by time - for (t, e) in &edges { - g.add_edge(*t, e.0, e.1, NO_PROPS, None).unwrap(); - } + let rand_start_index = rng().random_range(0..edges.len()); + let rand_end_index = rng().random_range(rand_start_index..edges.len()); - let start = edges.get(rand_start_index).expect("start index in range").0; - let end = edges.get(rand_end_index).expect("end index in range").0; + let g = Graph::new(); - let wg = g.window(start, end); + for (t, e) in &edges { + g.add_edge(*t, e.0, e.1, NO_PROPS, None).unwrap(); + } - let rand_test_index: usize = rng().random_range(0..edges.len()); + let start = edges.get(rand_start_index).expect("start index in range").0; + let end = edges.get(rand_end_index).expect("end index in range").0; - let (i, e) = edges.get(rand_test_index).expect("test index in range"); - if (start..end).contains(i) { - prop_assert!(wg.has_edge(e.0, e.1), "Edge {:?} was not in window {:?}", (i, e), start..end); - } else { - prop_assert!(!wg.has_edge(e.0, e.1), "Edge {:?} was in window {:?}", (i, e), start..end); - } - }); -} + let wg = g.window(start, end); -#[test] -fn windowed_graph_edge_count() { - proptest!(|(mut edges: Vec<(i64, (u64, u64))>, window: Range)| { - global_info_logger(); - prop_assume!(window.end >= window.start); + let rand_test_index: usize = rng().random_range(0..edges.len()); - edges.sort_by_key(|e| e.1); // Sorted by edge - edges.dedup_by_key(|e| e.1); // Have each edge only once to avoid headaches + let (i, e) = edges.get(rand_test_index).expect("test index in range"); + if (start..end).contains(i) { + prop_assert!(wg.has_edge(e.0, e.1), "Edge {:?} was not in window {:?}", (i, e), start..end); + } else { + prop_assert!(!wg.has_edge(e.0, e.1), "Edge {:?} was in window {:?}", (i, e), start..end); + } + }); + } - let true_edge_count = edges.iter().filter(|e| window.contains(&e.0)).count(); + #[test] + fn windowed_graph_edge_count() { + proptest!(|(mut edges: Vec<(i64, (u64, u64))>, window: Range)| { + global_info_logger(); + prop_assume!(window.end >= window.start); - let g = Graph::new(); + edges.sort_by_key(|e| e.1); // Sorted by edge + edges.dedup_by_key(|e| e.1); // Have each edge only once to avoid headaches - for (t, e) in &edges { - g.add_edge(*t, e.0, e.1, [("test".to_owned(), Prop::Bool(true))], None) - .unwrap(); - } + let true_edge_count = edges.iter().filter(|e| window.contains(&e.0)).count(); - let wg = g.window(window.start, window.end); - if wg.count_edges() != true_edge_count { - info!( - "failed, g.num_edges() = {}, true count = {}", - wg.count_edges(), - true_edge_count - ); - info!("g.edges() = {:?}", wg.edges().iter().collect_vec()); - } - prop_assert_eq!(wg.count_edges(), true_edge_count); - }); -} + let g = Graph::new(); -#[test] -fn trivial_window_has_all_edges() { - proptest!(|(edges: Vec<(i64, u64, u64)>)| { - let g = Graph::new(); - edges - .into_par_iter() - .filter(|e| e.0 < i64::MAX) - .for_each(|(t, src, dst)| { - g.add_edge(t, src, dst, [("test".to_owned(), Prop::Bool(true))], None) + for (t, e) in &edges { + g.add_edge(*t, e.0, e.1, [("test".to_owned(), Prop::Bool(true))], None) .unwrap(); - }); - let w = g.window(i64::MIN, i64::MAX); - prop_assert!(g.edges() - .iter() - .all(|e| w.has_edge(e.src().id(), e.dst().id()))); - }); -} + } -#[test] -fn large_node_in_window() { - proptest!(|(dsts: Vec)| { - let dsts: Vec = dsts.into_iter().unique().collect(); - let n = dsts.len(); - let g = Graph::new(); + let wg = g.window(window.start, window.end); + if wg.count_edges() != true_edge_count { + info!( + "failed, g.num_edges() = {}, true count = {}", + wg.count_edges(), + true_edge_count + ); + info!("g.edges() = {:?}", wg.edges().iter().collect_vec()); + } + prop_assert_eq!(wg.count_edges(), true_edge_count); + }); + } - for dst in dsts { - let t = 1; - g.add_edge(t, 0, dst, NO_PROPS, None).unwrap(); - } - let w = g.window(i64::MIN, i64::MAX); - prop_assert_eq!(w.count_edges(), n); - }); -} + #[test] + fn trivial_window_has_all_edges() { + proptest!(|(edges: Vec<(i64, u64, u64)>)| { + let g = Graph::new(); + edges + .into_par_iter() + .filter(|e| e.0 < i64::MAX) + .for_each(|(t, src, dst)| { + g.add_edge(t, src, dst, [("test".to_owned(), Prop::Bool(true))], None) + .unwrap(); + }); + let w = g.window(i64::MIN, i64::MAX); + prop_assert!(g.edges() + .iter() + .all(|e| w.has_edge(e.src().id(), e.dst().id()))); + }); + } + + #[test] + fn large_node_in_window() { + proptest!(|(dsts: Vec)| { + let dsts: Vec = dsts.into_iter().unique().collect(); + let n = dsts.len(); + let g = Graph::new(); -#[test] -fn windowed_graph_node_ids() { - let vs = vec![(1, 1, 2), (3, 3, 4), (5, 5, 6), (7, 7, 1)]; + for dst in dsts { + let t = 1; + g.add_edge(t, 0, dst, NO_PROPS, None).unwrap(); + } + let w = g.window(i64::MIN, i64::MAX); + prop_assert_eq!(w.count_edges(), n); + }); + } - let args = [(i64::MIN, 8), (i64::MIN, 2), (i64::MIN, 4), (3, 6)]; + #[test] + fn windowed_graph_node_ids() { + let vs = vec![(1, 1, 2), (3, 3, 4), (5, 5, 6), (7, 7, 1)]; - let expected = vec![ - vec![1, 2, 3, 4, 5, 6, 7], - vec![1, 2], - vec![1, 2, 3, 4], - vec![3, 4, 5, 6], - ]; + let args = [(i64::MIN, 8), (i64::MIN, 2), (i64::MIN, 4), (3, 6)]; - let graph = Graph::new(); + let expected = vec![ + vec![1, 2, 3, 4, 5, 6, 7], + vec![1, 2], + vec![1, 2, 3, 4], + vec![3, 4, 5, 6], + ]; - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } + let graph = Graph::new(); - test_storage!(&graph, |graph| { - let res: Vec<_> = (0..=3) - .map(|i| { - let wg = graph.window(args[i].0, args[i].1); - let mut e = wg - .nodes() - .id() - .iter_values() - .filter_map(|id| id.to_u64()) - .collect::>(); - e.sort(); - e - }) - .collect_vec(); - - assert_eq!(res, expected); - }); - - let graph = Graph::new(); - for (src, dst, t) in &vs { - graph.add_edge(*src, *dst, *t, NO_PROPS, None).unwrap(); + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let res: Vec<_> = (0..=3) + .map(|i| { + let wg = graph.window(args[i].0, args[i].1); + let mut e = wg + .nodes() + .id() + .iter_values() + .filter_map(|id| id.to_u64()) + .collect::>(); + e.sort(); + e + }) + .collect_vec(); + + assert_eq!(res, expected); + }); + + let graph = Graph::new(); + for (src, dst, t) in &vs { + graph.add_edge(*src, *dst, *t, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let res: Vec<_> = (0..=3) + .map(|i| { + let wg = graph.window(args[i].0, args[i].1); + let mut e = wg + .nodes() + .id() + .iter_values() + .filter_map(|id| id.to_u64()) + .collect::>(); + e.sort(); + e + }) + .collect_vec(); + assert_eq!(res, expected); + }); } - test_storage!(&graph, |graph| { - let res: Vec<_> = (0..=3) - .map(|i| { - let wg = graph.window(args[i].0, args[i].1); - let mut e = wg - .nodes() - .id() - .iter_values() - .filter_map(|id| id.to_u64()) - .collect::>(); - e.sort(); - e - }) - .collect_vec(); - assert_eq!(res, expected); - }); -} -#[test] -fn windowed_graph_nodes() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - graph - .add_node( - 0, - 1, - [("type", "wallet".into_prop()), ("cost", 99.5.into_prop())], - None, - None, - ) - .unwrap(); - - graph - .add_node( - -1, - 2, - [("type", "wallet".into_prop()), ("cost", 10.0.into_prop())], - None, - None, - ) - .unwrap(); - - graph - .add_node( - 6, - 3, - [("type", "wallet".into_prop()), ("cost", 76.2.into_prop())], - None, - None, - ) - .unwrap(); - - for (t, src, dst) in &vs { + #[test] + fn windowed_graph_nodes() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + graph - .add_edge(*t, *src, *dst, [("eprop", "commons")], None) + .add_node( + 0, + 1, + [("type", "wallet".into_prop()), ("cost", 99.5.into_prop())], + None, + None, + ) .unwrap(); - } - test_storage!(&graph, |graph| { - let wg = graph.window(-2, 0); - let actual = wg - .nodes() - .id() - .iter_values() - .filter_map(|id| id.to_u64()) - .collect::>(); + graph + .add_node( + -1, + 2, + [("type", "wallet".into_prop()), ("cost", 10.0.into_prop())], + None, + None, + ) + .unwrap(); - let expected = vec![1, 2]; + graph + .add_node( + 6, + 3, + [("type", "wallet".into_prop()), ("cost", 76.2.into_prop())], + None, + None, + ) + .unwrap(); - assert_eq!(actual, expected); - }); -} + for (t, src, dst) in &vs { + graph + .add_edge(*t, *src, *dst, [("eprop", "commons")], None) + .unwrap(); + } + test_storage!(&graph, |graph| { + let wg = graph.window(-2, 0); -#[test] -fn test_reference() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let mut w = WindowedGraph::new(&graph, Some(0.into_time()), Some(1.into_time())); - assert_eq!(w, graph); - w = WindowedGraph::new(&graph, Some(1.into_time()), Some(2.into_time())); - assert_eq!(w, Graph::new()); - }); -} + let actual = wg + .nodes() + .id() + .iter_values() + .filter_map(|id| id.to_u64()) + .collect::>(); -#[test] -fn test_algorithm_on_windowed_graph() { - global_info_logger(); - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - test_storage!(&graph, |graph| { - let w = graph.window(0, 1); - let _ = degree_centrality(&w); - }); -} + let expected = vec![1, 2]; -#[test] -fn test_view_resetting() { - let graph = Graph::new(); - for t in 0..10 { - let t1 = t * 3; - let t2 = t * 3 + 1; - let t3 = t * 3 + 2; - graph.add_edge(t1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(t2, 2, 3, NO_PROPS, None).unwrap(); - graph.add_edge(t3, 3, 1, NO_PROPS, None).unwrap(); + assert_eq!(actual, expected); + }); } - test_storage!(&graph, |graph| { - assert_graph_equal(&graph.before(9).after(2), &graph.window(3, 9)); - let res = graph - .window(3, 9) - .nodes() - .before(6) - .edges() - .window(1, 9) - .earliest_time() - .map(|it| it.map(|t_opt| t_opt.map(|t| t.t())).collect_vec()) - .collect_vec(); - assert_eq!( - res, - [[Some(3), Some(5)], [Some(3), Some(4)], [Some(5), Some(4)]] - ); - }); -} + #[test] + fn test_reference() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let mut w = WindowedGraph::new(&graph, Some(0.into_time()), Some(1.into_time())); + assert_eq!(w, graph); + w = WindowedGraph::new(&graph, Some(1.into_time()), Some(2.into_time())); + assert_eq!(w, Graph::new()); + }); + } -#[test] -fn test_entity_history() { - let graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(3, 0, NO_PROPS, None, None).unwrap(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(4, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(5, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(6, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(7, 1, 3, NO_PROPS, None).unwrap(); - - // FIXME: Issue #46 - test_graph(&graph, |graph| { - let e = graph.edge(1, 2).unwrap(); - let v = graph.node(0).unwrap(); - let full_history_1 = vec![0i64, 1, 2, 3]; - - let full_history_2 = vec![4i64, 5, 6, 7]; - - let windowed_history = vec![0i64, 1]; - - assert_eq!(v.history(), full_history_1); - - assert_eq!(v.window(0, 2).history(), windowed_history); - assert_eq!(e.history(), full_history_1); - assert_eq!(e.window(0, 2).history(), windowed_history); - - assert_eq!( - graph.edges().history().collect_vec(), - [full_history_1.clone(), full_history_2.clone()] - ); - assert_eq!( - graph - .nodes() - .in_edges() - .history() - .map(|it| it.collect_vec()) - .collect_vec(), - [vec![], vec![], vec![full_history_1], vec![full_history_2],] - ); - - assert_eq!( - graph - .nodes() - .earliest_time() - .iter_values() - .flatten() - .collect_vec(), - [0, 0, 0, 4,] - ); + #[test] + fn test_algorithm_on_windowed_graph() { + global_info_logger(); + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + let w = graph.window(0, 1); + let _ = degree_centrality(&w); + }); + } - assert_eq!( - graph - .nodes() - .latest_time() - .iter_values() - .flatten() - .collect_vec(), - [3, 7, 3, 7] - ); + #[test] + fn test_view_resetting() { + let graph = Graph::new(); + for t in 0..10 { + let t1 = t * 3; + let t2 = t * 3 + 1; + let t3 = t * 3 + 2; + graph.add_edge(t1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(t2, 2, 3, NO_PROPS, None).unwrap(); + graph.add_edge(t3, 3, 1, NO_PROPS, None).unwrap(); + } - assert_eq!( - graph + test_storage!(&graph, |graph| { + assert_graph_equal(&graph.before(9).after(2), &graph.window(3, 9)); + let res = graph + .window(3, 9) .nodes() - .neighbours() - .latest_time() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, it)| it.flatten().collect_vec()) - .collect_vec(), - [vec![], vec![3, 7], vec![7], vec![7],] - ); - - assert_eq!( - graph - .nodes() - .neighbours() + .before(6) + .edges() + .window(1, 9) .earliest_time() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, it)| it.flatten().collect_vec()) - .collect_vec(), - [vec![], vec![0, 4], vec![0], vec![0],] - ); - }); -} + .map(|it| it.map(|t_opt| t_opt.map(|t| t.t())).collect_vec()) + .collect_vec(); + assert_eq!( + res, + [[Some(3), Some(5)], [Some(3), Some(4)], [Some(5), Some(4)]] + ); + }); + } + + #[test] + fn test_entity_history() { + let graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(3, 0, NO_PROPS, None, None).unwrap(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(4, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(5, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(6, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(7, 1, 3, NO_PROPS, None).unwrap(); + + // FIXME: Issue #46 + test_graph(&graph, |graph| { + let e = graph.edge(1, 2).unwrap(); + let v = graph.node(0).unwrap(); + let full_history_1 = vec![0i64, 1, 2, 3]; + + let full_history_2 = vec![4i64, 5, 6, 7]; + + let windowed_history = vec![0i64, 1]; + + assert_eq!(v.history(), full_history_1); + + assert_eq!(v.window(0, 2).history(), windowed_history); + assert_eq!(e.history(), full_history_1); + assert_eq!(e.window(0, 2).history(), windowed_history); + + assert_eq!( + graph.edges().history().collect_vec(), + [full_history_1.clone(), full_history_2.clone()] + ); + assert_eq!( + graph + .nodes() + .in_edges() + .history() + .map(|it| it.collect_vec()) + .collect_vec(), + [vec![], vec![], vec![full_history_1], vec![full_history_2],] + ); + + assert_eq!( + graph + .nodes() + .earliest_time() + .iter_values() + .flatten() + .collect_vec(), + [0, 0, 0, 4,] + ); + + assert_eq!( + graph + .nodes() + .latest_time() + .iter_values() + .flatten() + .collect_vec(), + [3, 7, 3, 7] + ); + + assert_eq!( + graph + .nodes() + .neighbours() + .latest_time() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, it)| it.flatten().collect_vec()) + .collect_vec(), + [vec![], vec![3, 7], vec![7], vec![7],] + ); -mod test_filters_window_graph { + assert_eq!( + graph + .nodes() + .neighbours() + .earliest_time() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, it)| it.flatten().collect_vec()) + .collect_vec(), + [vec![], vec![0, 4], vec![0], vec![0],] + ); + }); + } - mod test_nodes_filters_window_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - TestGraphVariants, TestVariants, + mod test_filters_window_graph { + + mod test_nodes_filters_window_graph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::{ + assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, + TestGraphVariants, TestVariants, + }, + views::filter::model::ComposableFilter, }, - views::filter::model::ComposableFilter, }, - }, - prelude::{AdditionOps, PropertyAdditionOps}, - }; - use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; - use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, - }; - - use raphtory::{ - db::{ - api::view::filter_ops::Filter, - graph::{ - assertions::WindowGraphTransformer, - views::filter::model::{ - node_filter::{ops::NodeFilterOps, NodeFilter}, - property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, + prelude::{AdditionOps, PropertyAdditionOps}, + }; + use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; + use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, + property_addition_ops::InternalPropertyAdditionOps, + }; + + use raphtory::{ + db::{ + api::view::filter_ops::Filter, + graph::{ + assertions::WindowGraphTransformer, + views::filter::model::{ + node_filter::{ops::NodeFilterOps, NodeFilter}, + property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, + }, }, }, - }, - errors::GraphError, - prelude::{Graph, GraphViewOps, TimeOps}, - }; - - fn init_graph(graph: G) -> G { - let nodes = vec![ - ( - 6, - "N1", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ( - 7, - "N1", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(5i64)), - ("k3", Prop::Bool(false)), - ], - Some("air_nomad"), - ), - ( - 6, - "N2", - vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(6.0f64))], - Some("water_tribe"), - ), - ( - 7, - "N2", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("water_tribe"), - ), - (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - ( - 5, - "N5", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ( - 6, - "N5", - vec![ - ("p1", Prop::U64(2u64)), - ("k2", Prop::Str(ArcStr::from("Pometry"))), - ("k4", Prop::F64(1.0f64)), - ], - Some("air_nomad"), - ), - (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - ( - 6, - "N6", - vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(1.0f64))], - Some("fire_nation"), - ), - ( - 3, - "N7", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("air_nomad"), - ), - (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - ( - 4, - "N8", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("fire_nation"), - ), - (2, "N9", vec![("p1", Prop::U64(2u64))], None), - (2, "N10", vec![("q1", Prop::U64(0u64))], None), - (2, "N10", vec![("p1", Prop::U64(3u64))], None), - (2, "N11", vec![("p1", Prop::U64(3u64))], None), - (2, "N11", vec![("q1", Prop::U64(0u64))], None), - (2, "N12", vec![("q1", Prop::U64(0u64))], None), - ( - 3, - "N12", + errors::GraphError, + prelude::{Graph, GraphViewOps, TimeOps}, + }; + + fn init_graph( + graph: G, + ) -> G { + let nodes = vec![ + ( + 6, + "N1", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ( + 7, + "N1", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(5i64)), + ("k3", Prop::Bool(false)), + ], + Some("air_nomad"), + ), + ( + 6, + "N2", + vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(6.0f64))], + Some("water_tribe"), + ), + ( + 7, + "N2", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("water_tribe"), + ), + (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + ( + 5, + "N5", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ( + 6, + "N5", + vec![ + ("p1", Prop::U64(2u64)), + ("k2", Prop::Str(ArcStr::from("Pometry"))), + ("k4", Prop::F64(1.0f64)), + ], + Some("air_nomad"), + ), + (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + ( + 6, + "N6", + vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(1.0f64))], + Some("fire_nation"), + ), + ( + 3, + "N7", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("air_nomad"), + ), + (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + ( + 4, + "N8", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("fire_nation"), + ), + (2, "N9", vec![("p1", Prop::U64(2u64))], None), + (2, "N10", vec![("q1", Prop::U64(0u64))], None), + (2, "N10", vec![("p1", Prop::U64(3u64))], None), + (2, "N11", vec![("p1", Prop::U64(3u64))], None), + (2, "N11", vec![("q1", Prop::U64(0u64))], None), + (2, "N12", vec![("q1", Prop::U64(0u64))], None), + ( + 3, + "N12", + vec![ + ("p1", Prop::U64(3u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + None, + ), + (2, "N13", vec![("q1", Prop::U64(0u64))], None), + (3, "N13", vec![("p1", Prop::U64(3u64))], None), + (2, "N14", vec![("q1", Prop::U64(0u64))], None), + (2, "N15", vec![], None), + ]; + + // Add nodes to the graph + for (id, name, props, layer) in &nodes { + graph + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); + } + + // Metadata property assignments + let metadata = vec![ + ( + "N1", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(3i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + ), + ("N4", vec![("p1", Prop::U64(2u64))]), + ("N9", vec![("p1", Prop::U64(1u64))]), + ("N10", vec![("p1", Prop::U64(1u64))]), + ("N11", vec![("p1", Prop::U64(1u64))]), + ("N12", vec![("p1", Prop::U64(1u64))]), + ( + "N13", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + ), + ("N14", vec![("p1", Prop::U64(1u64))]), + ("N15", vec![("p1", Prop::U64(1u64))]), + ]; + + // Apply metadata + for (node, props) in metadata { + graph.node(node).unwrap().add_metadata(props).unwrap(); + } + + graph + } + + fn init_graph2< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes = vec![( + 2, + "N14", vec![ - ("p1", Prop::U64(3u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), + ("q1", Prop::U64(0u64)), + ( + "x", + Prop::list(vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)]), + ), ], None, - ), - (2, "N13", vec![("q1", Prop::U64(0u64))], None), - (3, "N13", vec![("p1", Prop::U64(3u64))], None), - (2, "N14", vec![("q1", Prop::U64(0u64))], None), - (2, "N15", vec![], None), - ]; - - // Add nodes to the graph - for (id, name, props, layer) in &nodes { + )]; + + // Add nodes to the graph + for (id, name, props, layer) in &nodes { + graph + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); + } + graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); } - // Metadata property assignments - let metadata = vec![ - ( - "N1", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(3i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - ), - ("N4", vec![("p1", Prop::U64(2u64))]), - ("N9", vec![("p1", Prop::U64(1u64))]), - ("N10", vec![("p1", Prop::U64(1u64))]), - ("N11", vec![("p1", Prop::U64(1u64))]), - ("N12", vec![("p1", Prop::U64(1u64))]), - ( - "N13", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - ), - ("N14", vec![("p1", Prop::U64(1u64))]), - ("N15", vec![("p1", Prop::U64(1u64))]), - ]; - - // Apply metadata - for (node, props) in metadata { - graph.node(node).unwrap().add_metadata(props).unwrap(); + #[test] + fn test_nodes_filters_for_node_name_eq() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::name().eq("N2"); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); } - graph - } + #[test] + fn test_nodes_filters_pg_for_node_name_eq() { + let filter = NodeFilter::name().eq("N2"); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - fn init_graph2< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let nodes = vec![( - 2, - "N14", - vec![ - ("q1", Prop::U64(0u64)), - ( - "x", - Prop::list(vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)]), - ), - ], - None, - )]; + #[test] + fn test_nodes_filters_for_node_name_ne() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::name().ne("N2"); + let expected_results = vec!["N1", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - // Add nodes to the graph - for (id, name, props, layer) in &nodes { - graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); + #[test] + fn test_nodes_filters_pg_for_node_name_ne() { + let filter = NodeFilter::name().ne("N2"); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N3", "N5", "N6", "N7", "N8", + "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); } - graph - } + #[test] + fn test_nodes_filters_for_node_name_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::name().is_in(vec!["N2"]); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter::name().is_in(vec!["N2", "N5"]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_nodes_filters_for_node_name_eq() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::name().eq("N2"); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + #[test] + fn test_nodes_filters_pg_for_node_name_in() { + let filter = NodeFilter::name().is_in(vec!["N2"]); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter::name().is_in(vec!["N2", "N5"]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_nodes_filters_pg_for_node_name_eq() { - let filter = NodeFilter::name().eq("N2"); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_nodes_filters_for_node_name_not_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::name().is_not_in(vec!["N5"]); + let expected_results = vec!["N1", "N2", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_nodes_filters_for_node_name_ne() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::name().ne("N2"); - let expected_results = vec!["N1", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } + #[test] + fn test_nodes_filters_pg_for_node_name_not_in() { + let filter = NodeFilter::name().is_not_in(vec!["N5"]); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N6", "N7", "N8", + "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_nodes_filters_pg_for_node_name_ne() { - let filter = NodeFilter::name().ne("N2"); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N3", "N5", "N6", "N7", "N8", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_nodes_filters_for_node_type_eq() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::node_type().eq("fire_nation"); + let expected_results = vec!["N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_nodes_filters_for_node_name_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::name().is_in(vec!["N2"]); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_nodes_filters_pg_for_node_type_eq() { + let filter = NodeFilter::node_type().eq("fire_nation"); + let expected_results = vec!["N6", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = NodeFilter::name().is_in(vec!["N2", "N5"]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + #[test] + fn test_nodes_filters_for_node_type_ne() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::node_type().ne("fire_nation"); + let expected_results = vec!["N1", "N2", "N3", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_nodes_filters_pg_for_node_name_in() { - let filter = NodeFilter::name().is_in(vec!["N2"]); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_nodes_filters_pg_for_node_type_ne() { + let filter = NodeFilter::node_type().ne("fire_nation"); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N5", "N7", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = NodeFilter::name().is_in(vec!["N2", "N5"]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_nodes_filters_for_node_type_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); + let expected_results = vec!["N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomad"]); + let expected_results = vec!["N1", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_nodes_filters_for_node_name_not_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::name().is_not_in(vec!["N5"]); - let expected_results = vec!["N1", "N2", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } + #[test] + fn test_nodes_filters_pg_for_node_type_in() { + let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); + let expected_results = vec!["N6", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomad"]); + let expected_results = vec!["N1", "N3", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_nodes_filters_pg_for_node_name_not_in() { - let filter = NodeFilter::name().is_not_in(vec!["N5"]); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N6", "N7", "N8", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_nodes_filters_for_node_type_not_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); + let expected_results = vec!["N1", "N2", "N3", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_nodes_filters_for_node_type_eq() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::node_type().eq("fire_nation"); - let expected_results = vec!["N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } + #[test] + fn test_nodes_filters_pg_for_node_type_not_in() { + let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N5", "N7", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_nodes_filters_pg_for_node_type_eq() { - let filter = NodeFilter::node_type().eq("fire_nation"); - let expected_results = vec!["N6", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_nodes_filters_for_property_eq() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").eq(2i64); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k2").eq("Paper_Airplane"); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter.property("k3").eq(true); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter.property("k4").eq(6.0f64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter.property("x").eq(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = vec!["N14"]; + // TODO: List(U64) not supported as disk_graph property + // assert_filter_nodes_results_w!( + // init_graph2, + // filter, + // 1..9, + // expected_results, + // variants = [graph] + // ); + assert_filter_nodes_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + // TODO: Search APIs don't support list yet + // assert_search_nodes_results( + // init_graph, + // WindowGraphTransformer(6..9), + // filter, + // &expected_results, + // TestVariants::EventOnly, + // ); + } - #[test] - fn test_nodes_filters_for_node_type_ne() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::node_type().ne("fire_nation"); - let expected_results = vec!["N1", "N2", "N3", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } + #[test] + fn test_nodes_filters_pg_for_property_eq() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").eq(2i64); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k2").eq("Paper_Airplane"); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + // TODO: Const properties not supported for disk_graph. + let filter = NodeFilter.property("k3").eq(true); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").eq(6.0f64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("x").eq(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = vec!["N14"]; + // TODO: List(U64) not supported as disk_graph property + // assert_filter_nodes_results_pg_w!( + // init_graph2, + // filter, + // 1..9, + // expected_results, + // variants = [persistent_graph] + // ); + assert_filter_nodes_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + // TODO: Search APIs don't support list yet + // assert_search_nodes_results( + // init_graph, + // WindowGraphTransformer(6..9), + // filter, + // &expected_results, + // vec![TestGraphVariants::PersistentGraph], + // ); + } - #[test] - fn test_nodes_filters_pg_for_node_type_ne() { - let filter = NodeFilter::node_type().ne("fire_nation"); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N5", "N7", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_nodes_filters_for_property_ne() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").ne(1u64); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").ne(2i64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k2").ne("Paper_Airplane"); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k3").ne(true); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").ne(6.0f64); + let expected_results = vec!["N2", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_nodes_filters_for_node_type_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); - let expected_results = vec!["N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); + #[test] + fn test_nodes_filters_pg_for_property_ne() { + let filter = NodeFilter.property("p1").ne(1u64); + let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").ne(2i64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k2").ne("Paper_Airplane"); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k3").ne(true); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").ne(6.0f64); + let expected_results = vec!["N12", "N2", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomad"]); - let expected_results = vec!["N1", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } + #[test] + fn test_nodes_filters_for_property_lt() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").lt(3u64); + let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").lt(3i64); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").lt(10.0f64); + let expected_results = vec!["N1", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_nodes_filters_pg_for_node_type_in() { - let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); - let expected_results = vec!["N6", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_nodes_filters_pg_for_property_lt() { + let filter = NodeFilter.property("p1").lt(3u64); + let expected_results = vec!["N1", "N2", "N3", "N5", "N6", "N7", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").lt(3i64); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").lt(10.0f64); + let expected_results = vec!["N1", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomad"]); - let expected_results = vec!["N1", "N3", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_nodes_filters_for_property_le() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").le(2i64); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").le(6.0f64); + let expected_results = vec!["N1", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_nodes_filters_for_node_type_not_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); - let expected_results = vec!["N1", "N2", "N3", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_node_type_not_in() { - let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N5", "N7", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_eq() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").eq(2i64); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k2").eq("Paper_Airplane"); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = NodeFilter.property("k3").eq(true); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = NodeFilter.property("k4").eq(6.0f64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = NodeFilter.property("x").eq(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = vec!["N14"]; - // TODO: List(U64) not supported as disk_graph property - // assert_filter_nodes_results_w!( - // init_graph2, - // filter, - // 1..9, - // expected_results, - // variants = [graph] - // ); - assert_filter_nodes_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - // TODO: Search APIs don't support list yet - // assert_search_nodes_results( - // init_graph, - // WindowGraphTransformer(6..9), - // filter, - // &expected_results, - // TestVariants::EventOnly, - // ); - } - - #[test] - fn test_nodes_filters_pg_for_property_eq() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").eq(2i64); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k2").eq("Paper_Airplane"); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - // TODO: Const properties not supported for disk_graph. - let filter = NodeFilter.property("k3").eq(true); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").eq(6.0f64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("x").eq(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = vec!["N14"]; - // TODO: List(U64) not supported as disk_graph property - // assert_filter_nodes_results_pg_w!( - // init_graph2, - // filter, - // 1..9, - // expected_results, - // variants = [persistent_graph] - // ); - assert_filter_nodes_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - // TODO: Search APIs don't support list yet - // assert_search_nodes_results( - // init_graph, - // WindowGraphTransformer(6..9), - // filter, - // &expected_results, - // vec![TestGraphVariants::PersistentGraph], - // ); - } - - #[test] - fn test_nodes_filters_for_property_ne() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").ne(1u64); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").ne(2i64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k2").ne("Paper_Airplane"); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k3").ne(true); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").ne(6.0f64); - let expected_results = vec!["N2", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_ne() { - let filter = NodeFilter.property("p1").ne(1u64); - let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").ne(2i64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k2").ne("Paper_Airplane"); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k3").ne(true); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").ne(6.0f64); - let expected_results = vec!["N12", "N2", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_lt() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").lt(3u64); - let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").lt(3i64); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").lt(10.0f64); - let expected_results = vec!["N1", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_lt() { - let filter = NodeFilter.property("p1").lt(3u64); - let expected_results = vec!["N1", "N2", "N3", "N5", "N6", "N7", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").lt(3i64); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").lt(10.0f64); - let expected_results = vec!["N1", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_le() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").le(2i64); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").le(6.0f64); - let expected_results = vec!["N1", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_le() { - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").le(2i64); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").le(6.0f64); - let expected_results = vec!["N1", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_gt() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").gt(2i64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").gt(6.0f64); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("x").gt(Prop::List( - vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)].into(), - )); - let graph = init_graph(Graph::new()); - assert!(matches!( - graph.window(1, 9).filter(filter.clone()).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "x" - )); - assert!(matches!( - graph.persistent_graph().window(1, 9).filter(filter).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "x" - )); - } - - #[test] - fn test_nodes_filters_pg_for_property_gt() { - let filter = NodeFilter.property("p1").gt(1u64); - let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").gt(2i64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").gt(6.0f64); - let expected_results = vec!["N12", "N2", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_ge() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").ge(1u64); - let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").ge(2i64); - let expected_results = vec!["N1", "N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").ge(6.0f64); - let expected_results = vec!["N1", "N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_ge() { - let filter = NodeFilter.property("p1").ge(1u64); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N5", "N6", "N7", "N8", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").ge(2i64); - let expected_results = vec!["N1", "N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").ge(6.0f64); - let expected_results = vec!["N1", "N12", "N2", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").is_in(vec![2u64.into()]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").is_in(vec![2i64.into()]); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter - .property("k2") - .is_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k3").is_in(vec![true.into()]); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").is_in(vec![6.0f64.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_in() { - let filter = NodeFilter.property("p1").is_in(vec![2u64.into()]); - let expected_results = vec!["N2", "N5", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").is_in(vec![2i64.into()]); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter - .property("k2") - .is_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - // TODO: Const properties not supported for disk_graph. - let filter = NodeFilter.property("k3").is_in(vec![true.into()]); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").is_in(vec![6.0f64.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_not_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").is_not_in(vec![1u64.into()]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").is_not_in(vec![2i64.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter - .property("k2") - .is_not_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k3").is_not_in(vec![true.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").is_not_in(vec![6.0f64.into()]); - let expected_results = vec!["N2", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_not_in() { - let filter = NodeFilter.property("p1").is_not_in(vec![1u64.into()]); - let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").is_not_in(vec![2i64.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter - .property("k2") - .is_not_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k3").is_not_in(vec![true.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").is_not_in(vec![6.0f64.into()]); - let expected_results = vec!["N12", "N2", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_is_some() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").is_some(); - let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(1..2), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(1..2), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(10..12), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(10..12), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_is_some() { - let filter = NodeFilter.property("p1").is_some(); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N5", "N6", "N7", "N8", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(1..2), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(1..2), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(10..12), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(10..12), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_props_added_at_different_times() { - let filter = NodeFilter - .property("q1") - .eq(0u64) - .and(NodeFilter.property("p1").eq(3u64)); - let expected_results = vec!["N10", "N11", "N12", "N13"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(1..4), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(1..4), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_pg_for_props_added_at_different_times() { - let filter = NodeFilter - .property("q1") - .eq(0u64) - .and(NodeFilter.property("p1").eq(3u64)); - let expected_results = vec!["N10", "N11", "N12", "N13"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_fuzzy_search() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter - .property("k2") - .fuzzy_search("Paper_Airpla", 2, false); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_fuzzy_search() { - let filter = NodeFilter - .property("k2") - .fuzzy_search("Paper_Air", 5, false); - let expected_results = vec!["N1", "N2", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_fuzzy_search_prefix_match() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec!["N1", "N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, false); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_fuzzy_search_prefix_match() { - let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec!["N1", "N2", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, false); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - - mod test_edges_filters_window_graph { - use raphtory::{ - db::{ - api::view::{filter_ops::Filter, StaticGraphViewOps}, - graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, - TestGraphVariants, TestVariants, WindowGraphTransformer, - }, - views::filter::model::{ - edge_filter::EdgeFilter, node_filter::ops::NodeFilterOps, - property_filter::ops::PropertyFilterOps, ComposableFilter, - PropertyFilterFactory, - }, - }, - }, - errors::GraphError, - prelude::{AdditionOps, Graph, GraphViewOps, PropertyAdditionOps, TimeOps, NO_PROPS}, - }; - use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; - - fn init_graph(graph: G) -> G { - let edges = vec![ - ( - 6, - "N1", - "N2", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ( - 7, - "N1", - "N2", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(5i64)), - ("k3", Prop::Bool(false)), - ], - Some("air_nomad"), - ), - ( - 6, - "N2", - "N3", - vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(6.0f64))], - Some("water_tribe"), - ), - ( - 7, - "N2", - "N3", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("water_tribe"), - ), - ( - 8, - "N3", - "N4", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - ( - 9, - "N4", - "N5", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - ( - 5, - "N5", - "N6", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ( - 6, - "N5", - "N6", - vec![ - ("p1", Prop::U64(2u64)), - ("k2", Prop::Str(ArcStr::from("Pometry"))), - ("k4", Prop::F64(1.0f64)), - ], - Some("air_nomad"), - ), - ( - 5, - "N6", - "N7", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - ( - 6, - "N6", - "N7", - vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(1.0f64))], - Some("fire_nation"), - ), - ( - 3, - "N7", - "N8", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("air_nomad"), - ), - ( - 5, - "N7", - "N8", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - ( - 3, - "N8", - "N9", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - ( - 4, - "N8", - "N9", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("fire_nation"), - ), - (2, "N9", "N10", vec![("p1", Prop::U64(2u64))], None), - (2, "N10", "N11", vec![("q1", Prop::U64(0u64))], None), - (2, "N10", "N11", vec![("p1", Prop::U64(3u64))], None), - (2, "N11", "N12", vec![("p1", Prop::U64(3u64))], None), - (2, "N11", "N12", vec![("q1", Prop::U64(0u64))], None), - (2, "N12", "N13", vec![("q1", Prop::U64(0u64))], None), - ( - 3, - "N12", - "N13", - vec![ - ("p1", Prop::U64(3u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - None, - ), - (2, "N13", "N14", vec![("q1", Prop::U64(0u64))], None), - (3, "N13", "N14", vec![("p1", Prop::U64(3u64))], None), - (2, "N14", "N15", vec![("q1", Prop::U64(0u64))], None), - (2, "N15", "N1", vec![], None), - ]; - - for (id, src, dst, props, layer) in &edges { - graph - .add_edge(*id, src, dst, props.clone(), *layer) - .unwrap(); - } - - // Metadata property assignments - let metadata = vec![ - ( - "N1", - "N2", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(3i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ("N4", "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - ("N9", "N10", vec![("p1", Prop::U64(1u64))], None), - ("N10", "N11", vec![("p1", Prop::U64(1u64))], None), - ("N11", "N12", vec![("p1", Prop::U64(1u64))], None), - ("N12", "N13", vec![("p1", Prop::U64(1u64))], None), - ( - "N13", - "N14", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - None, - ), - ("N14", "N15", vec![("p1", Prop::U64(1u64))], None), - ("N15", "N1", vec![("p1", Prop::U64(1u64))], None), - ]; - - for (src, dst, props, layer) in metadata { - graph - .edge(src, dst) - .unwrap() - .add_metadata(props, layer) - .unwrap(); - } - - graph.add_node(1, "N1", NO_PROPS, None, None).unwrap(); - graph.add_node(2, "N2", NO_PROPS, None, None).unwrap(); - graph.add_node(3, "N3", NO_PROPS, None, None).unwrap(); - - graph - } - - fn init_graph2(graph: G) -> G { - let edges = vec![( - 2, - "N14", - "N15", - vec![ - ("q1", Prop::U64(0u64)), - ( - "x", - Prop::list(vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)]), - ), - ], - None, - )]; - - for (id, src, dst, props, layer) in &edges { - graph - .add_edge(*id, src, dst, props.clone(), *layer) - .unwrap(); - } - - graph - } - - #[test] - fn test_edges_filters_for_src_eq() { - let filter = EdgeFilter::src().name().eq("N2"); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_src_eq() { - let filter = EdgeFilter::src().name().eq("N2"); - let expected_results = vec!["N2->N3"]; - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_src_ne() { - let filter = EdgeFilter::src().name().ne("N2"); - let expected_results = vec!["N1->N2", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_src_ne() { - let filter = EdgeFilter::src().name().ne("N2"); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", "N15->N1", - "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", - ]; - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - } - - #[test] - fn test_edges_filters_for_dst_in() { - let filter = EdgeFilter::dst().name().is_in(vec!["N2"]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter::dst().name().is_in(vec!["N2", "N5"]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_dst_in() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter::dst().name().is_in(vec!["N2"]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter::dst().name().is_in(vec!["N2", "N5"]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_dst_not_in() { - let filter = EdgeFilter::dst().name().is_not_in(vec!["N5"]); - let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_dst_not_in() { - let filter = EdgeFilter::dst().name().is_not_in(vec!["N5"]); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", "N15->N1", - "N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", - ]; - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_property_eq() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").eq(2i64); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k2").eq("Paper_Airplane"); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k3").eq(true); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").eq(6.0f64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("x").eq(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = vec!["N14->N15"]; - // TODO: List(U64) not supported as disk_graph property - // assert_filter_edges_results_w!( - // init_graph2, - // filter, - // 1..9, - // expected_results, - // variants = [graph] - // ); - assert_filter_edges_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - // TODO: Search APIs don't support list yet - // assert_search_edges_results( - // init_graph2, - // WindowGraphTransformer(6..9), - // filter, - // &expected_results, - // TestVariants::PersistentOnly, - // ); - } - - #[test] - fn test_edges_filters_pg_for_property_eq() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").eq(2i64); - - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k2").eq("Paper_Airplane"); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k3").eq(true); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").eq(6.0f64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("x").eq(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = vec!["N14->N15"]; - // TODO: List(U64) not supported as disk_graph property - // assert_filter_edges_results_pg_w!( - // init_graph2, - // filter, - // 1..9, - // expected_results, - // variants = [] - // ); - assert_filter_edges_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - // TODO: Search APIs don't support list yet - // assert_search_edges_results( - // init_graph2, - // WindowGraphTransformer(1..9), - // filter.clone(), - // &expected_results, - // vec![TestGraphVariants::PersistentGraph], - // ); - } - - #[test] - fn test_edges_filters_for_property_ne() { - let filter = EdgeFilter.property("p1").ne(1u64); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").ne(2i64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k2").ne("Paper_Airplane"); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k3").ne(true); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").ne(6.0f64); - let expected_results = vec!["N2->N3", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_property_ne() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").ne(1u64); - let expected_results = vec![ - "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", - "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").ne(2i64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k2").ne("Paper_Airplane"); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k3").ne(true); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").ne(6.0f64); - let expected_results = - vec!["N12->N13", "N2->N3", "N5->N6", "N6->N7", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("x").ne(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = Vec::<&str>::new(); - assert_filter_edges_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - // TODO: Search APIs don't support list yet - // assert_search_edges_results( - // init_graph2, - // WindowGraphTransformer(1..9), - // filter.clone(), - // &expected_results, - // TestVariants::PersistentOnly, - // ); - } + #[test] + fn test_nodes_filters_pg_for_property_le() { + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").le(2i64); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").le(6.0f64); + let expected_results = vec!["N1", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_edges_filters_for_property_lt() { - let filter = EdgeFilter.property("p1").lt(3u64); - let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_nodes_filters_for_property_gt() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").gt(2i64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").gt(6.0f64); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("x").gt(Prop::List( + vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)].into(), + )); + let graph = init_graph(Graph::new()); + assert!(matches!( + graph.window(1, 9).filter(filter.clone()).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "x" + )); + assert!(matches!( + graph.persistent_graph().window(1, 9).filter(filter).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "x" + )); + } - let filter = EdgeFilter.property("k1").lt(3i64); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_nodes_filters_pg_for_property_gt() { + let filter = NodeFilter.property("p1").gt(1u64); + let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").gt(2i64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").gt(6.0f64); + let expected_results = vec!["N12", "N2", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter.property("k4").lt(10.0f64); - let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + #[test] + fn test_nodes_filters_for_property_ge() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").ge(1u64); + let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").ge(2i64); + let expected_results = vec!["N1", "N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").ge(6.0f64); + let expected_results = vec!["N1", "N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_edges_filters_pg_for_property_lt() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").lt(3u64); - let expected_results = vec![ - "N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_nodes_filters_pg_for_property_ge() { + let filter = NodeFilter.property("p1").ge(1u64); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N5", "N6", "N7", "N8", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").ge(2i64); + let expected_results = vec!["N1", "N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").ge(6.0f64); + let expected_results = vec!["N1", "N12", "N2", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter.property("k1").lt(3i64); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_nodes_filters_for_property_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").is_in(vec![2u64.into()]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").is_in(vec![2i64.into()]); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter + .property("k2") + .is_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k3").is_in(vec![true.into()]); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").is_in(vec![6.0f64.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - let filter = EdgeFilter.property("k4").lt(10.0f64); - let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_nodes_filters_pg_for_property_in() { + let filter = NodeFilter.property("p1").is_in(vec![2u64.into()]); + let expected_results = vec!["N2", "N5", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").is_in(vec![2i64.into()]); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter + .property("k2") + .is_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + // TODO: Const properties not supported for disk_graph. + let filter = NodeFilter.property("k3").is_in(vec![true.into()]); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").is_in(vec![6.0f64.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_edges_filters_for_property_le() { - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_nodes_filters_for_property_not_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").is_not_in(vec![1u64.into()]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").is_not_in(vec![2i64.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter + .property("k2") + .is_not_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k3").is_not_in(vec![true.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").is_not_in(vec![6.0f64.into()]); + let expected_results = vec!["N2", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - let filter = EdgeFilter.property("k1").le(2i64); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_nodes_filters_pg_for_property_not_in() { + let filter = NodeFilter.property("p1").is_not_in(vec![1u64.into()]); + let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").is_not_in(vec![2i64.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter + .property("k2") + .is_not_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k3").is_not_in(vec![true.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").is_not_in(vec![6.0f64.into()]); + let expected_results = vec!["N12", "N2", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter.property("k4").le(6.0f64); - let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + #[test] + fn test_nodes_filters_for_property_is_some() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").is_some(); + let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(1..2), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(1..2), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(10..12), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(10..12), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - #[test] - fn test_edges_filters_pg_for_property_le() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_nodes_filters_pg_for_property_is_some() { + let filter = NodeFilter.property("p1").is_some(); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N5", "N6", "N7", "N8", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(1..2), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(1..2), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N4", "N5", "N6", "N7", "N8", + "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(10..12), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(10..12), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter.property("k1").le(2i64); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_nodes_filters_for_props_added_at_different_times() { + let filter = NodeFilter + .property("q1") + .eq(0u64) + .and(NodeFilter.property("p1").eq(3u64)); + let expected_results = vec!["N10", "N11", "N12", "N13"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(1..4), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(1..4), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let filter = EdgeFilter.property("k4").le(6.0f64); - let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_nodes_filters_pg_for_props_added_at_different_times() { + let filter = NodeFilter + .property("q1") + .eq(0u64) + .and(NodeFilter.property("p1").eq(3u64)); + let expected_results = vec!["N10", "N11", "N12", "N13"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_edges_filters_for_property_gt() { - let filter = EdgeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_nodes_filters_fuzzy_search() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter + .property("k2") + .fuzzy_search("Paper_Airpla", 2, false); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - let filter = EdgeFilter.property("k1").gt(2i64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_nodes_filters_pg_fuzzy_search() { + let filter = NodeFilter + .property("k2") + .fuzzy_search("Paper_Air", 5, false); + let expected_results = vec!["N1", "N2", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter.property("k4").gt(6.0f64); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_nodes_filters_fuzzy_search_prefix_match() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec!["N1", "N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, false); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } - let filter = EdgeFilter.property("x").gt(Prop::List( - vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)].into(), - )); - let graph = init_graph(Graph::new()); - assert!(matches!( - graph.window(1, 9).filter(filter.clone()).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "x" - )); - assert!(matches!( - graph.persistent_graph().window(1, 9).filter(filter).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "x" - )); + #[test] + fn test_nodes_filters_pg_fuzzy_search_prefix_match() { + let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec!["N1", "N2", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, false); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } } - #[test] - fn test_edges_filters_pg_for_property_gt() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").gt(1u64); - let expected_results = vec![ - "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", - "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").gt(2i64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").gt(6.0f64); - let expected_results = vec!["N12->N13", "N2->N3", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + mod test_edges_filters_window_graph { + use raphtory::{ + db::{ + api::view::{filter_ops::Filter, StaticGraphViewOps}, + graph::{ + assertions::{ + assert_filter_edges_results, assert_search_edges_results, + TestGraphVariants, TestVariants, WindowGraphTransformer, + }, + views::filter::model::{ + edge_filter::EdgeFilter, node_filter::ops::NodeFilterOps, + property_filter::ops::PropertyFilterOps, ComposableFilter, + PropertyFilterFactory, + }, + }, + }, + errors::GraphError, + prelude::{ + AdditionOps, Graph, GraphViewOps, PropertyAdditionOps, TimeOps, NO_PROPS, + }, + }; + use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; - #[test] - fn test_edges_filters_for_property_ge() { - let filter = EdgeFilter.property("p1").ge(1u64); - let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + fn init_graph( + graph: G, + ) -> G { + let edges = vec![ + ( + 6, + "N1", + "N2", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ( + 7, + "N1", + "N2", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(5i64)), + ("k3", Prop::Bool(false)), + ], + Some("air_nomad"), + ), + ( + 6, + "N2", + "N3", + vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(6.0f64))], + Some("water_tribe"), + ), + ( + 7, + "N2", + "N3", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("water_tribe"), + ), + ( + 8, + "N3", + "N4", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + ( + 9, + "N4", + "N5", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + ( + 5, + "N5", + "N6", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ( + 6, + "N5", + "N6", + vec![ + ("p1", Prop::U64(2u64)), + ("k2", Prop::Str(ArcStr::from("Pometry"))), + ("k4", Prop::F64(1.0f64)), + ], + Some("air_nomad"), + ), + ( + 5, + "N6", + "N7", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + ( + 6, + "N6", + "N7", + vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(1.0f64))], + Some("fire_nation"), + ), + ( + 3, + "N7", + "N8", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("air_nomad"), + ), + ( + 5, + "N7", + "N8", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + ( + 3, + "N8", + "N9", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + ( + 4, + "N8", + "N9", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("fire_nation"), + ), + (2, "N9", "N10", vec![("p1", Prop::U64(2u64))], None), + (2, "N10", "N11", vec![("q1", Prop::U64(0u64))], None), + (2, "N10", "N11", vec![("p1", Prop::U64(3u64))], None), + (2, "N11", "N12", vec![("p1", Prop::U64(3u64))], None), + (2, "N11", "N12", vec![("q1", Prop::U64(0u64))], None), + (2, "N12", "N13", vec![("q1", Prop::U64(0u64))], None), + ( + 3, + "N12", + "N13", + vec![ + ("p1", Prop::U64(3u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + None, + ), + (2, "N13", "N14", vec![("q1", Prop::U64(0u64))], None), + (3, "N13", "N14", vec![("p1", Prop::U64(3u64))], None), + (2, "N14", "N15", vec![("q1", Prop::U64(0u64))], None), + (2, "N15", "N1", vec![], None), + ]; + + for (id, src, dst, props, layer) in &edges { + graph + .add_edge(*id, src, dst, props.clone(), *layer) + .unwrap(); + } + + // Metadata property assignments + let metadata = vec![ + ( + "N1", + "N2", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(3i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ("N4", "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + ("N9", "N10", vec![("p1", Prop::U64(1u64))], None), + ("N10", "N11", vec![("p1", Prop::U64(1u64))], None), + ("N11", "N12", vec![("p1", Prop::U64(1u64))], None), + ("N12", "N13", vec![("p1", Prop::U64(1u64))], None), + ( + "N13", + "N14", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + None, + ), + ("N14", "N15", vec![("p1", Prop::U64(1u64))], None), + ("N15", "N1", vec![("p1", Prop::U64(1u64))], None), + ]; + + for (src, dst, props, layer) in metadata { + graph + .edge(src, dst) + .unwrap() + .add_metadata(props, layer) + .unwrap(); + } + + graph.add_node(1, "N1", NO_PROPS, None, None).unwrap(); + graph.add_node(2, "N2", NO_PROPS, None, None).unwrap(); + graph.add_node(3, "N3", NO_PROPS, None, None).unwrap(); - let filter = EdgeFilter.property("k1").ge(2i64); - let expected_results = vec!["N1->N2", "N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + graph + } - let filter = EdgeFilter.property("k4").ge(6.0f64); - let expected_results = vec!["N1->N2", "N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + fn init_graph2( + graph: G, + ) -> G { + let edges = vec![( + 2, + "N14", + "N15", + vec![ + ("q1", Prop::U64(0u64)), + ( + "x", + Prop::list(vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)]), + ), + ], + None, + )]; - #[test] - fn test_edges_filters_pg_for_property_ge() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").ge(1u64); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N3->N4", - "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + for (id, src, dst, props, layer) in &edges { + graph + .add_edge(*id, src, dst, props.clone(), *layer) + .unwrap(); + } - let filter = EdgeFilter.property("k1").ge(2i64); - let expected_results = - vec!["N1->N2", "N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + graph + } - let filter = EdgeFilter.property("k4").ge(6.0f64); - let expected_results = vec!["N1->N2", "N12->N13", "N2->N3", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_edges_filters_for_src_eq() { + let filter = EdgeFilter::src().name().eq("N2"); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_filters_for_property_in() { - let filter = EdgeFilter.property("p1").is_in(vec![2u64.into()]); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_edges_filters_pg_for_src_eq() { + let filter = EdgeFilter::src().name().eq("N2"); + let expected_results = vec!["N2->N3"]; + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter.property("k1").is_in(vec![2i64.into()]); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_edges_filters_for_src_ne() { + let filter = EdgeFilter::src().name().ne("N2"); + let expected_results = vec!["N1->N2", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let filter = EdgeFilter - .property("k2") - .is_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_edges_filters_pg_for_src_ne() { + let filter = EdgeFilter::src().name().ne("N2"); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", + "N15->N1", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", + ]; + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + } - let filter = EdgeFilter.property("k3").is_in(vec![true.into()]); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_edges_filters_for_dst_in() { + let filter = EdgeFilter::dst().name().is_in(vec!["N2"]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter::dst().name().is_in(vec!["N2", "N5"]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let filter = EdgeFilter.property("k4").is_in(vec![6.0f64.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + #[test] + fn test_edges_filters_pg_for_dst_in() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter::dst().name().is_in(vec!["N2"]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter::dst().name().is_in(vec!["N2", "N5"]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_edges_filters_pg_for_property_in() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").is_in(vec![2u64.into()]); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N9", "N9->N10"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_for_dst_not_in() { + let filter = EdgeFilter::dst().name().is_not_in(vec!["N5"]); + let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let filter = EdgeFilter.property("k1").is_in(vec![2i64.into()]); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_pg_for_dst_not_in() { + let filter = EdgeFilter::dst().name().is_not_in(vec!["N5"]); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", + "N15->N1", "N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", + "N9->N10", + ]; + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter - .property("k2") - .is_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_for_property_eq() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").eq(2i64); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k2").eq("Paper_Airplane"); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k3").eq(true); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").eq(6.0f64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("x").eq(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = vec!["N14->N15"]; + // TODO: List(U64) not supported as disk_graph property + // assert_filter_edges_results_w!( + // init_graph2, + // filter, + // 1..9, + // expected_results, + // variants = [graph] + // ); + assert_filter_edges_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + // TODO: Search APIs don't support list yet + // assert_search_edges_results( + // init_graph2, + // WindowGraphTransformer(6..9), + // filter, + // &expected_results, + // TestVariants::PersistentOnly, + // ); + } - let filter = EdgeFilter.property("k3").is_in(vec![true.into()]); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_pg_for_property_eq() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").eq(2i64); + + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k2").eq("Paper_Airplane"); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k3").eq(true); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").eq(6.0f64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("x").eq(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = vec!["N14->N15"]; + // TODO: List(U64) not supported as disk_graph property + // assert_filter_edges_results_pg_w!( + // init_graph2, + // filter, + // 1..9, + // expected_results, + // variants = [] + // ); + assert_filter_edges_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + // TODO: Search APIs don't support list yet + // assert_search_edges_results( + // init_graph2, + // WindowGraphTransformer(1..9), + // filter.clone(), + // &expected_results, + // vec![TestGraphVariants::PersistentGraph], + // ); + } - let filter = EdgeFilter.property("k4").is_in(vec![6.0f64.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_edges_filters_for_property_ne() { + let filter = EdgeFilter.property("p1").ne(1u64); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").ne(2i64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k2").ne("Paper_Airplane"); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k3").ne(true); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").ne(6.0f64); + let expected_results = vec!["N2->N3", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_filters_for_property_not_in() { - let filter = EdgeFilter.property("p1").is_not_in(vec![1u64.into()]); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_edges_filters_pg_for_property_ne() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").ne(1u64); + let expected_results = vec![ + "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", + "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").ne(2i64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k2").ne("Paper_Airplane"); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k3").ne(true); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").ne(6.0f64); + let expected_results = + vec!["N12->N13", "N2->N3", "N5->N6", "N6->N7", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("x").ne(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = Vec::<&str>::new(); + assert_filter_edges_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + // TODO: Search APIs don't support list yet + // assert_search_edges_results( + // init_graph2, + // WindowGraphTransformer(1..9), + // filter.clone(), + // &expected_results, + // TestVariants::PersistentOnly, + // ); + } - let filter = EdgeFilter.property("k1").is_not_in(vec![2i64.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_edges_filters_for_property_lt() { + let filter = EdgeFilter.property("p1").lt(3u64); + let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").lt(3i64); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").lt(10.0f64); + let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let filter = EdgeFilter - .property("k2") - .is_not_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_edges_filters_pg_for_property_lt() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").lt(3u64); + let expected_results = vec![ + "N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").lt(3i64); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").lt(10.0f64); + let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter.property("k3").is_not_in(vec![true.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_edges_filters_for_property_le() { + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").le(2i64); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").le(6.0f64); + let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let filter = EdgeFilter.property("k4").is_not_in(vec![6.0f64.into()]); - let expected_results = vec!["N2->N3", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + #[test] + fn test_edges_filters_pg_for_property_le() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").le(2i64); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").le(6.0f64); + let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_edges_filters_pg_for_property_not_in() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").is_not_in(vec![1u64.into()]); - let expected_results = vec![ - "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", - "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_for_property_gt() { + let filter = EdgeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").gt(2i64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").gt(6.0f64); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("x").gt(Prop::List( + vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)].into(), + )); + let graph = init_graph(Graph::new()); + assert!(matches!( + graph.window(1, 9).filter(filter.clone()).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "x" + )); + assert!(matches!( + graph.persistent_graph().window(1, 9).filter(filter).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "x" + )); + } - let filter = EdgeFilter.property("k1").is_not_in(vec![2i64.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_pg_for_property_gt() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").gt(1u64); + let expected_results = vec![ + "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", + "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").gt(2i64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").gt(6.0f64); + let expected_results = vec!["N12->N13", "N2->N3", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter - .property("k2") - .is_not_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_for_property_ge() { + let filter = EdgeFilter.property("p1").ge(1u64); + let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").ge(2i64); + let expected_results = vec!["N1->N2", "N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").ge(6.0f64); + let expected_results = vec!["N1->N2", "N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let filter = EdgeFilter.property("k3").is_not_in(vec![true.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_pg_for_property_ge() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").ge(1u64); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N3->N4", + "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").ge(2i64); + let expected_results = + vec!["N1->N2", "N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").ge(6.0f64); + let expected_results = vec!["N1->N2", "N12->N13", "N2->N3", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } - let filter = EdgeFilter.property("k4").is_not_in(vec![6.0f64.into()]); - let expected_results = - vec!["N12->N13", "N2->N3", "N5->N6", "N6->N7", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_edges_filters_for_property_in() { + let filter = EdgeFilter.property("p1").is_in(vec![2u64.into()]); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").is_in(vec![2i64.into()]); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter + .property("k2") + .is_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k3").is_in(vec![true.into()]); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").is_in(vec![6.0f64.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_filters_for_property_is_some() { - let filter = EdgeFilter.property("p1").is_some(); - let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + #[test] + fn test_edges_filters_pg_for_property_in() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").is_in(vec![2u64.into()]); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N9", "N9->N10"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").is_in(vec![2i64.into()]); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter + .property("k2") + .is_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k3").is_in(vec![true.into()]); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").is_in(vec![6.0f64.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_edges_filters_pg_for_property_is_some() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").is_some(); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N3->N4", - "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_edges_filters_for_property_not_in() { + let filter = EdgeFilter.property("p1").is_not_in(vec![1u64.into()]); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").is_not_in(vec![2i64.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter + .property("k2") + .is_not_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k3").is_not_in(vec![true.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").is_not_in(vec![6.0f64.into()]); + let expected_results = vec!["N2->N3", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - fn test_edges_filters_for_src_dst() { - let filter = EdgeFilter::src() - .name() - .eq("N1") - .and(EdgeFilter::dst().name().eq("N2")); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::All, - ); - } + #[test] + fn test_edges_filters_pg_for_property_not_in() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").is_not_in(vec![1u64.into()]); + let expected_results = vec![ + "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", + "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").is_not_in(vec![2i64.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter + .property("k2") + .is_not_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k3").is_not_in(vec![true.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").is_not_in(vec![6.0f64.into()]); + let expected_results = + vec!["N12->N13", "N2->N3", "N5->N6", "N6->N7", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - fn test_edges_filters_fuzzy_search() { - let filter = EdgeFilter - .property("k2") - .fuzzy_search("Paper_Airpla", 2, false); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + #[test] + fn test_edges_filters_for_property_is_some() { + let filter = EdgeFilter.property("p1").is_some(); + let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - #[test] - #[ignore] - fn test_edges_filters_pg_fuzzy_search() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("k2").fuzzy_search("Paper_", 2, false); - let expected_results = vec!["N1->N2", "N2->N3", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_pg_for_property_is_some() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").is_some(); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N3->N4", + "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } + #[test] + fn test_edges_filters_for_src_dst() { + let filter = EdgeFilter::src() + .name() + .eq("N1") + .and(EdgeFilter::dst().name().eq("N2")); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::All, + ); + } - #[test] - fn test_edges_filters_fuzzy_search_prefix_match() { - let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec!["N1->N2", "N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); + #[test] + fn test_edges_filters_fuzzy_search() { + let filter = EdgeFilter + .property("k2") + .fuzzy_search("Paper_Airpla", 2, false); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec!["N1->N2", "N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } + #[test] + #[ignore] + fn test_edges_filters_pg_fuzzy_search() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("k2").fuzzy_search("Paper_", 2, false); + let expected_results = vec!["N1->N2", "N2->N3", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } - #[test] - #[ignore] - fn test_edges_filters_pg_fuzzy_search_prefix_match() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec![ - "N1->N2", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N7->N8", "N8->N9", - ]; - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + fn test_edges_filters_fuzzy_search_prefix_match() { + let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec!["N1->N2", "N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec!["N1->N2", "N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } - let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, false); - let expected_results = Vec::<&str>::new(); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); + #[test] + #[ignore] + fn test_edges_filters_pg_fuzzy_search_prefix_match() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec![ + "N1->N2", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N7->N8", "N8->N9", + ]; + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, false); + let expected_results = Vec::<&str>::new(); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } } } } From a8bb1ac8014f15dae2aaae2851f6ad5c25414230 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Tue, 19 May 2026 16:42:34 +0100 Subject: [PATCH 3/8] move most tests outside of raphtory --- Cargo.toml | 4 +- raphtory-graphql/Cargo.toml | 4 +- raphtory-test-utils/Cargo.toml | 36 + .../resources/test/test.csv | 0 .../src}/assertions.rs | 6 +- raphtory-test-utils/src/lib.rs | 2 + .../src/test_utils.rs | 14 +- .../tests/algo_tests/centrality.rs | 311 + .../tests/algo_tests/community_detection.rs | 216 + .../tests/algo_tests/components.rs | 949 ++ .../tests/algo_tests/cores.rs | 4 +- .../tests/algo_tests/embeddings.rs | 3 +- .../tests/algo_tests/metrics.rs | 21 +- .../tests/algo_tests/mod.rs | 8 + .../tests/algo_tests/motifs.rs | 17 +- .../tests/algo_tests/pathing.rs | 11 +- .../tests/algorithms.rs | 0 raphtory-test-utils/tests/cached_view.rs | 370 + raphtory-test-utils/tests/db_tests.rs | 3895 +++++ .../tests/deletion_graph.rs | 0 .../tests/df_loaders.rs | 20 +- .../tests/edge_property_filter.rs | 381 + .../tests/exploded_edge_property_filter.rs | 677 + .../tests/node_property_filter.rs | 458 + raphtory-test-utils/tests/node_test.rs | 433 + .../tests/proto_test.rs | 0 .../tests/serialise_test.rs | 0 raphtory-test-utils/tests/subgraph_tests.rs | 559 + raphtory-test-utils/tests/test_deletions.rs | 1184 ++ raphtory-test-utils/tests/test_edge.rs | 189 + raphtory-test-utils/tests/test_edge_view.rs | 162 + .../tests/test_exploded_edges.rs | 551 + raphtory-test-utils/tests/test_filters.rs | 12325 +++++++++++++++ .../tests/test_history.rs | 0 raphtory-test-utils/tests/test_layers.rs | 778 + raphtory-test-utils/tests/test_materialize.rs | 194 + .../tests/test_materialize_sf10.rs | 21 +- .../tests_node_type_filtered_subgraph.rs | 674 + raphtory-test-utils/tests/time_tests.rs | 331 + raphtory-test-utils/tests/valid_graph.rs | 379 + raphtory-test-utils/tests/views_test.rs | 4581 ++++++ raphtory/Cargo.toml | 8 +- raphtory/examples/eth_loader.rs | 1 + .../graph/storage_ops/property_schema.rs | 1 - raphtory/src/db/graph/mod.rs | 2 - raphtory/src/db/graph/views/deletion_graph.rs | 5 +- .../db/graph/views/property_redacted_graph.rs | 5 +- raphtory/src/lib.rs | 3 - raphtory/tests/algo_tests/centrality.rs | 314 - .../tests/algo_tests/community_detection.rs | 220 - raphtory/tests/algo_tests/components.rs | 954 -- raphtory/tests/cached_view.rs | 378 - raphtory/tests/db_tests.rs | 3901 ----- raphtory/tests/edge_property_filter.rs | 384 - .../tests/exploded_edge_property_filter.rs | 678 - raphtory/tests/history_filter.rs | 1 - raphtory/tests/node_property_filter.rs | 459 - raphtory/tests/node_test.rs | 437 - raphtory/tests/subgraph_tests.rs | 568 - raphtory/tests/test_deletions.rs | 1186 -- raphtory/tests/test_edge.rs | 193 - raphtory/tests/test_edge_view.rs | 164 - raphtory/tests/test_exploded_edges.rs | 553 - raphtory/tests/test_filters.rs | 12355 ---------------- raphtory/tests/test_layers.rs | 787 - raphtory/tests/test_materialize.rs | 193 - .../tests_node_type_filtered_subgraph.rs | 697 - raphtory/tests/time_tests.rs | 336 - raphtory/tests/valid_graph.rs | 382 - raphtory/tests/views_test.rs | 4607 ------ 70 files changed, 29698 insertions(+), 29842 deletions(-) create mode 100644 raphtory-test-utils/Cargo.toml rename {raphtory => raphtory-test-utils}/resources/test/test.csv (100%) rename {raphtory/src/db/graph => raphtory-test-utils/src}/assertions.rs (99%) create mode 100644 raphtory-test-utils/src/lib.rs rename {raphtory => raphtory-test-utils}/src/test_utils.rs (99%) create mode 100644 raphtory-test-utils/tests/algo_tests/centrality.rs create mode 100644 raphtory-test-utils/tests/algo_tests/community_detection.rs create mode 100644 raphtory-test-utils/tests/algo_tests/components.rs rename {raphtory => raphtory-test-utils}/tests/algo_tests/cores.rs (95%) rename {raphtory => raphtory-test-utils}/tests/algo_tests/embeddings.rs (99%) rename {raphtory => raphtory-test-utils}/tests/algo_tests/metrics.rs (95%) rename {raphtory => raphtory-test-utils}/tests/algo_tests/mod.rs (88%) rename {raphtory => raphtory-test-utils}/tests/algo_tests/motifs.rs (97%) rename {raphtory => raphtory-test-utils}/tests/algo_tests/pathing.rs (98%) rename {raphtory => raphtory-test-utils}/tests/algorithms.rs (100%) create mode 100644 raphtory-test-utils/tests/cached_view.rs create mode 100644 raphtory-test-utils/tests/db_tests.rs rename {raphtory => raphtory-test-utils}/tests/deletion_graph.rs (100%) rename {raphtory => raphtory-test-utils}/tests/df_loaders.rs (98%) create mode 100644 raphtory-test-utils/tests/edge_property_filter.rs create mode 100644 raphtory-test-utils/tests/exploded_edge_property_filter.rs create mode 100644 raphtory-test-utils/tests/node_property_filter.rs create mode 100644 raphtory-test-utils/tests/node_test.rs rename {raphtory => raphtory-test-utils}/tests/proto_test.rs (100%) rename {raphtory => raphtory-test-utils}/tests/serialise_test.rs (100%) create mode 100644 raphtory-test-utils/tests/subgraph_tests.rs create mode 100644 raphtory-test-utils/tests/test_deletions.rs create mode 100644 raphtory-test-utils/tests/test_edge.rs create mode 100644 raphtory-test-utils/tests/test_edge_view.rs create mode 100644 raphtory-test-utils/tests/test_exploded_edges.rs create mode 100644 raphtory-test-utils/tests/test_filters.rs rename {raphtory => raphtory-test-utils}/tests/test_history.rs (100%) create mode 100644 raphtory-test-utils/tests/test_layers.rs create mode 100644 raphtory-test-utils/tests/test_materialize.rs rename {raphtory => raphtory-test-utils}/tests/test_materialize_sf10.rs (97%) create mode 100644 raphtory-test-utils/tests/tests_node_type_filtered_subgraph.rs create mode 100644 raphtory-test-utils/tests/time_tests.rs create mode 100644 raphtory-test-utils/tests/valid_graph.rs create mode 100644 raphtory-test-utils/tests/views_test.rs delete mode 100644 raphtory/tests/algo_tests/centrality.rs delete mode 100644 raphtory/tests/algo_tests/community_detection.rs delete mode 100644 raphtory/tests/algo_tests/components.rs delete mode 100644 raphtory/tests/cached_view.rs delete mode 100644 raphtory/tests/db_tests.rs delete mode 100644 raphtory/tests/edge_property_filter.rs delete mode 100644 raphtory/tests/exploded_edge_property_filter.rs delete mode 100644 raphtory/tests/history_filter.rs delete mode 100644 raphtory/tests/node_property_filter.rs delete mode 100644 raphtory/tests/node_test.rs delete mode 100644 raphtory/tests/subgraph_tests.rs delete mode 100644 raphtory/tests/test_deletions.rs delete mode 100644 raphtory/tests/test_edge.rs delete mode 100644 raphtory/tests/test_edge_view.rs delete mode 100644 raphtory/tests/test_exploded_edges.rs delete mode 100644 raphtory/tests/test_filters.rs delete mode 100644 raphtory/tests/test_layers.rs delete mode 100644 raphtory/tests/test_materialize.rs delete mode 100644 raphtory/tests/tests_node_type_filtered_subgraph.rs delete mode 100644 raphtory/tests/time_tests.rs delete mode 100644 raphtory/tests/valid_graph.rs delete mode 100644 raphtory/tests/views_test.rs diff --git a/Cargo.toml b/Cargo.toml index 201bb3b6aa..0494af6608 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,11 +13,12 @@ members = [ "raphtory-core", "raphtory-storage", "raphtory-api-macros", + "raphtory-test-utils", "raphtory-itertools", "clam-core", "clam-core/snb", "raphtory-itertools" -] +, "raphtory-test-utils"] default-members = ["raphtory"] exclude = ["optd"] resolver = "2" @@ -62,6 +63,7 @@ opt-level = 1 # much smaller files db4-graph = { version = "0.18.0", path = "db4-graph", default-features = false } db4-storage = { version = "0.18.0", path = "db4-storage" } raphtory = { version = "0.18.0", path = "raphtory", default-features = false } +raphtory-test-utils = { version = "0.18.0", path = "raphtory-test-utils", default-features = false } raphtory-api = { version = "0.18.0", path = "raphtory-api", default-features = false } raphtory-api-macros = { version = "0.18.0", path = "raphtory-api-macros", default-features = false } raphtory-core = { version = "0.18.0", path = "raphtory-core", default-features = false } diff --git a/raphtory-graphql/Cargo.toml b/raphtory-graphql/Cargo.toml index 081ea82f76..04232dc7e6 100644 --- a/raphtory-graphql/Cargo.toml +++ b/raphtory-graphql/Cargo.toml @@ -63,13 +63,11 @@ zip = { workspace = true } clap = { workspace = true } rust-embed = { workspace = true } - - [dev-dependencies] parking_lot = { workspace = true } tempfile = { workspace = true } pretty_assertions = { workspace = true } -raphtory = { workspace = true, features = ["test-utils"] } +raphtory-test-utils = { workspace = true } arrow-array = { workspace = true } [features] diff --git a/raphtory-test-utils/Cargo.toml b/raphtory-test-utils/Cargo.toml new file mode 100644 index 0000000000..d163873fa9 --- /dev/null +++ b/raphtory-test-utils/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "raphtory-test-utils" +publish = false +version.workspace = true +documentation.workspace = true +repository.workspace = true +license.workspace = true +readme.workspace = true +homepage.workspace = true +keywords.workspace = true +authors.workspace = true +rust-version.workspace = true +edition.workspace = true + +[dependencies] +raphtory-api.workspace = true +raphtory = {workspace = true, features = ["io"]} +ahash.workspace = true +proptest.workspace = true +proptest-derive.workspace = true +serde.workspace = true +serde_json.workspace = true +itertools.workspace = true +storage.workspace = true +raphtory-storage.workspace = true +rand.workspace = true +rayon.workspace = true +bigdecimal.workspace = true +chrono.workspace = true + +[dev-dependencies] +tempfile.workspace = true +arrow.workspace = true +parquet.workspace = true +tracing.workspace = true +zip.workspace = true diff --git a/raphtory/resources/test/test.csv b/raphtory-test-utils/resources/test/test.csv similarity index 100% rename from raphtory/resources/test/test.csv rename to raphtory-test-utils/resources/test/test.csv diff --git a/raphtory/src/db/graph/assertions.rs b/raphtory-test-utils/src/assertions.rs similarity index 99% rename from raphtory/src/db/graph/assertions.rs rename to raphtory-test-utils/src/assertions.rs index 30b31dc1f9..74f16f78d9 100644 --- a/raphtory/src/db/graph/assertions.rs +++ b/raphtory-test-utils/src/assertions.rs @@ -1,4 +1,4 @@ -use crate::{ +use raphtory::{ db::api::view::{filter_ops::Filter, StaticGraphViewOps}, prelude::{EdgeViewOps, Graph, GraphViewOps, NodeViewOps}, }; @@ -7,8 +7,8 @@ use std::ops::Range; #[cfg(feature = "search")] pub use crate::db::api::view::SearchableGraphOps; #[cfg(feature = "search")] -use crate::prelude::IndexMutationOps; -use crate::{ +use raphtory::prelude::IndexMutationOps; +use raphtory::{ db::{ api::view::filter_ops::{EdgeSelect, NodeSelect}, graph::views::{ diff --git a/raphtory-test-utils/src/lib.rs b/raphtory-test-utils/src/lib.rs new file mode 100644 index 0000000000..843bb9f8a0 --- /dev/null +++ b/raphtory-test-utils/src/lib.rs @@ -0,0 +1,2 @@ +pub mod assertions; +pub mod test_utils; diff --git a/raphtory/src/test_utils.rs b/raphtory-test-utils/src/test_utils.rs similarity index 99% rename from raphtory/src/test_utils.rs rename to raphtory-test-utils/src/test_utils.rs index a2df97d642..bc5ea41cea 100644 --- a/raphtory/src/test_utils.rs +++ b/raphtory-test-utils/src/test_utils.rs @@ -1,10 +1,3 @@ -use crate::{ - db::{ - api::storage::storage::Storage, - graph::{edge::EdgeView, node::NodeView}, - }, - prelude::*, -}; use ahash::HashSet; use bigdecimal::BigDecimal; use chrono::{DateTime, NaiveDateTime, Utc}; @@ -12,6 +5,13 @@ use itertools::Itertools; use proptest::{arbitrary::any, prelude::*}; use proptest_derive::Arbitrary; use rand::seq::SliceRandom; +use raphtory::{ + db::{ + api::storage::storage::Storage, + graph::{edge::EdgeView, node::NodeView}, + }, + prelude::*, +}; use raphtory_api::core::{ entities::properties::prop::{PropType, DECIMAL_MAX}, storage::{ diff --git a/raphtory-test-utils/tests/algo_tests/centrality.rs b/raphtory-test-utils/tests/algo_tests/centrality.rs new file mode 100644 index 0000000000..882dc0dee4 --- /dev/null +++ b/raphtory-test-utils/tests/algo_tests/centrality.rs @@ -0,0 +1,311 @@ +use std::collections::HashMap; + +use crate::algo_tests::assert_eq_hashmaps_approx; +use itertools::Itertools; +use raphtory::{ + algorithms::centrality::{ + betweenness::betweenness_centrality, degree_centrality::degree_centrality, hits::hits, + pagerank::unweighted_page_rank, + }, + prelude::*, +}; +use raphtory_test_utils::test_storage; + +#[test] +fn test_betweenness_centrality() { + let graph = Graph::new(); + let vs = vec![ + (1, 2), + (1, 3), + (1, 4), + (2, 3), + (2, 4), + (2, 5), + (3, 4), + (3, 5), + (3, 6), + (4, 3), + (4, 2), + (4, 4), + ]; + for (src, dst) in &vs { + graph.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let mut expected: HashMap = HashMap::new(); + expected.insert("1".to_string(), 0.0); + expected.insert("2".to_string(), 1.0); + expected.insert("3".to_string(), 4.0); + expected.insert("4".to_string(), 1.0); + expected.insert("5".to_string(), 0.0); + expected.insert("6".to_string(), 0.0); + + let res = betweenness_centrality(graph, None, false) + .to_hashmap(|value| value.betweenness_centrality); + assert_eq!(res, expected); + + let mut expected: HashMap = HashMap::new(); + expected.insert("1".to_string(), 0.0); + expected.insert("2".to_string(), 0.05); + expected.insert("3".to_string(), 0.2); + expected.insert("4".to_string(), 0.05); + expected.insert("5".to_string(), 0.0); + expected.insert("6".to_string(), 0.0); + let res = betweenness_centrality(graph, None, true) + .to_hashmap(|value| value.betweenness_centrality); + assert_eq!(res, expected); + }); +} + +#[test] +fn test_degree_centrality() { + let graph = Graph::new(); + let vs = vec![(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]; + for (src, dst) in &vs { + graph.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let mut expected: HashMap = HashMap::new(); + expected.insert("1".to_string(), 1.0); + expected.insert("2".to_string(), 1.0); + expected.insert("3".to_string(), 2.0 / 3.0); + expected.insert("4".to_string(), 2.0 / 3.0); + + let res = degree_centrality(graph).to_hashmap(|value| value.score); + assert_eq!(res, expected); + }); +} + +#[test] +fn test_hits() { + let edges = vec![ + (1, 4), + (2, 3), + (2, 5), + (3, 1), + (4, 2), + (4, 3), + (5, 2), + (5, 3), + (5, 4), + (5, 6), + (6, 3), + (6, 8), + (7, 1), + (7, 3), + (8, 1), + ]; + let graph = Graph::new(); + + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let results = hits(graph, 20, None); + + let expected = HashMap::from([ + ("1".to_string(), (0.0431365, 0.096625775)), + ("2".to_string(), (0.14359662, 0.18366566)), + ("3".to_string(), (0.030866561, 0.36886504)), + ("4".to_string(), (0.1865414, 0.12442485)), + ("5".to_string(), (0.26667944, 0.05943252)), + ("6".to_string(), (0.14359662, 0.10755368)), + ("7".to_string(), (0.15471625, 0.0)), + ("8".to_string(), (0.030866561, 0.05943252)), + ]); + + assert_eq!(results.len(), 8); + for (node, value) in results.iter() { + let expected_value = expected.get(&node.name()).unwrap(); + assert!( + (value.hub_score - expected_value.0).abs() < 1e-6, + "mismatched hub score for node {}, expected {}, actual {}", + node.name(), + expected_value.0, + value.hub_score + ); + assert!( + (value.auth_score - expected_value.1).abs() < 1e-6, + "mismatched authority score for node {}, expected {}, actual {}", + node.name(), + expected_value.1, + value.auth_score + ); + } + }) +} + +#[test] +fn test_page_rank() { + let graph = Graph::new(); + + let edges = vec![(1, 2), (1, 4), (2, 3), (3, 1), (4, 1)]; + + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let expected: HashMap = HashMap::from([ + ("1".to_string(), 0.38694), + ("2".to_string(), 0.20195), + ("3".to_string(), 0.20916), + ("4".to_string(), 0.20195), + ]); + let results = unweighted_page_rank(graph, Some(1000), Some(1), None, true, None) + .to_hashmap(|value| value.score); + assert_eq_hashmaps_approx(&results, &expected, 1e-5); + }); +} + +#[test] +fn motif_page_rank() { + let edges = vec![ + (1, 2, 1), + (1, 3, 2), + (1, 4, 3), + (3, 1, 4), + (3, 4, 5), + (3, 5, 6), + (4, 5, 7), + (5, 6, 8), + (5, 8, 9), + (7, 5, 10), + (8, 5, 11), + (1, 9, 12), + (9, 1, 13), + (6, 3, 14), + (4, 8, 15), + (8, 3, 16), + (5, 10, 17), + (10, 5, 18), + (10, 8, 19), + (1, 11, 20), + (11, 1, 21), + (9, 11, 22), + (11, 9, 23), + ]; + + let graph = Graph::new(); + + for (src, dst, t) in edges { + graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let expected: HashMap = HashMap::from([ + ("10".to_string(), 0.072082), + ("8".to_string(), 0.136473), + ("3".to_string(), 0.15484), + ("6".to_string(), 0.07208), + ("11".to_string(), 0.06186), + ("2".to_string(), 0.03557), + ("1".to_string(), 0.11284), + ("4".to_string(), 0.07944), + ("7".to_string(), 0.01638), + ("9".to_string(), 0.06186), + ("5".to_string(), 0.19658), + ]); + + let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None) + .to_hashmap(|value| value.score); + + assert_eq_hashmaps_approx(&results, &expected, 1e-5); + }); +} + +#[test] +fn two_nodes_page_rank() { + let edges = vec![(1, 2), (2, 1)]; + + let graph = Graph::new(); + + for (t, (src, dst)) in edges.into_iter().enumerate() { + graph.add_edge(t as i64, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let expected: HashMap = + HashMap::from([("1".to_string(), 0.5), ("2".to_string(), 0.5)]); + + let results = unweighted_page_rank(graph, Some(1000), Some(4), None, false, None) + .to_hashmap(|value| value.score); + + assert_eq_hashmaps_approx(&results, &expected, 1e-3); + }); +} + +#[test] +fn three_nodes_page_rank_one_dangling() { + let edges = vec![(1, 2), (2, 1), (2, 3)]; + + let graph = Graph::new(); + + for (t, (src, dst)) in edges.into_iter().enumerate() { + graph.add_edge(t as i64, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let expected: HashMap = HashMap::from([ + ("1".to_string(), 0.303), + ("2".to_string(), 0.393), + ("3".to_string(), 0.303), + ]); + + let results = unweighted_page_rank(graph, Some(10), Some(4), None, false, None) + .to_hashmap(|value| value.score); + + assert_eq_hashmaps_approx(&results, &expected, 1e-3); + }); +} + +#[test] +fn dangling_page_rank() { + let edges = vec![ + (1, 2), + (1, 3), + (2, 3), + (3, 1), + (3, 2), + (3, 4), + // dangling from here + (4, 5), + (5, 6), + (6, 7), + (7, 8), + (8, 9), + (9, 10), + (10, 11), + ] + .into_iter() + .enumerate() + .map(|(t, (src, dst))| (src, dst, t as i64)) + .collect_vec(); + + let graph = Graph::new(); + + for (src, dst, t) in edges { + graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let expected: HashMap = HashMap::from([ + ("1".to_string(), 0.055), + ("2".to_string(), 0.079), + ("3".to_string(), 0.113), + ("4".to_string(), 0.055), + ("5".to_string(), 0.070), + ("6".to_string(), 0.083), + ("7".to_string(), 0.093), + ("8".to_string(), 0.102), + ("9".to_string(), 0.110), + ("10".to_string(), 0.117), + ("11".to_string(), 0.122), + ]); + + let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None) + .to_hashmap(|value| value.score); + + assert_eq_hashmaps_approx(&results, &expected, 1e-3); + }); +} diff --git a/raphtory-test-utils/tests/algo_tests/community_detection.rs b/raphtory-test-utils/tests/algo_tests/community_detection.rs new file mode 100644 index 0000000000..3f7a444743 --- /dev/null +++ b/raphtory-test-utils/tests/algo_tests/community_detection.rs @@ -0,0 +1,216 @@ +use raphtory::{ + algorithms::community_detection::{ + label_propagation::label_propagation, + louvain::louvain, + modularity::{ComID, ModularityFunction, ModularityUnDir, Partition}, + }, + core::entities::VID, + logging::global_info_logger, + prelude::*, +}; +use raphtory_test_utils::test_storage; +use std::collections::{HashMap, HashSet}; +use tracing::info; + +fn group_by_value(map: &HashMap) -> Vec> { + let mut grouped: HashMap> = HashMap::new(); + + for (key, &value) in map { + grouped + .entry(value) + .or_insert_with(HashSet::new) + .insert(key.clone()); + } + + grouped.into_values().collect() +} + +#[test] +fn lpa_test() { + let graph: Graph = Graph::new(); + let edges = vec![ + (1, "R1", "R2"), + (1, "R1", "R3"), + (1, "R2", "R3"), + (1, "R3", "G"), + (1, "G", "B1"), + (1, "G", "B3"), + (1, "B1", "B2"), + (1, "B2", "B3"), + (1, "B2", "B4"), + (1, "B3", "B4"), + (1, "B3", "B5"), + (1, "B4", "B5"), + ]; + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let seed = Some([5; 32]); + let result = + label_propagation(graph, 20, seed, None).to_hashmap(|value| value.community_id); + println!("{:?}", result); + let result = group_by_value(&result); + + let expected = vec![ + HashSet::from(["R1".to_string(), "R2".to_string(), "R3".to_string()]), + HashSet::from([ + "G".to_string(), + "B1".to_string(), + "B2".to_string(), + "B3".to_string(), + "B4".to_string(), + "B5".to_string(), + ]), + ]; + for hashset in expected { + assert!(result.contains(&hashset)); + } + }); +} + +use proptest::prelude::*; + +#[test] +fn test_louvain() { + let edges = vec![ + (100, 200, 2.0f64), + (100, 300, 3.0f64), + (200, 300, 8.5f64), + (300, 400, 1.0f64), + (400, 500, 1.5f64), + (600, 800, 0.5f64), + (700, 900, 3.5f64), + (100, 600, 1.5f64), + ]; + test_all_nodes_assigned_inner(edges) +} + +fn test_all_nodes_assigned_inner(edges: Vec<(u64, u64, f64)>) { + let graph = Graph::new(); + for (src, dst, weight) in edges { + graph + .add_edge(1, src, dst, [("weight", weight)], None) + .unwrap(); + graph + .add_edge(1, dst, src, [("weight", weight)], None) + .unwrap(); + } + + test_storage!(&graph, |graph| { + let result = louvain::(graph, 1.0, Some("weight"), None); + assert!(graph + .nodes() + .iter() + .all(|n| result.get_by_node(n).is_some())); + }); +} + +fn test_all_nodes_assigned_inner_unweighted(edges: Vec<(u64, u64)>) { + let graph = Graph::new(); + for (src, dst) in edges { + graph.add_edge(1, src, dst, NO_PROPS, None).unwrap(); + graph.add_edge(1, dst, src, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let result = louvain::(graph, 1.0, None, None); + assert!(graph + .nodes() + .iter() + .all(|n| result.get_by_node(n).is_some())); + }); +} + +proptest! { + #[test] + fn test_all_nodes_in_communities(edges in any::>().prop_map(|mut v| {v.iter_mut().for_each(|(_, _, w)| *w = w.abs()); v})) { + test_all_nodes_assigned_inner(edges) + } + + #[test] + fn test_all_nodes_assigned_unweighted(edges in any::>().prop_map(|v| v.into_iter().map(|(s, d)| (s as u64, d as u64)).collect::>())) { + test_all_nodes_assigned_inner_unweighted(edges) + } +} + +#[test] +fn lfr_test() { + use raphtory::io::csv_loader::CsvLoader; + use raphtory_api::core::utils::logging::global_info_logger; + use serde::{Deserialize, Serialize}; + use std::path::PathBuf; + global_info_logger(); + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.push("resources/test"); + let loader = CsvLoader::new(d.join("test.csv")).set_delimiter(","); + let graph = Graph::new(); + + #[derive(Deserialize, Serialize, Debug)] + struct CsvEdge { + src: u64, + dst: u64, + } + + loader + .load_into_graph(&graph, |e: CsvEdge, g| { + g.add_edge(1, e.src, e.dst, NO_PROPS, None).unwrap(); + }) + .unwrap(); + + test_storage!(&graph, |graph| { + let _ = louvain::(graph, 1.0, None, None); + // TODO: Add assertions + }); +} + +#[test] +fn test_delta() { + global_info_logger(); + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let mut m = ModularityUnDir::new( + graph, + None, + 1.0, + Partition::new_singletons(graph.count_nodes()), + 1e-8, + ); + let old_value = m.value(); + assert_eq!(old_value, -0.5); + let delta = m.move_delta(&VID(0), ComID(1)); + info!("delta: {delta}"); + m.move_node(&VID(0), ComID(1)); + assert_eq!(m.value(), old_value + delta) + }); +} + +#[test] +fn test_aggregation() { + global_info_logger(); + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 0, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); + graph.add_edge(0, 0, 3, NO_PROPS, None).unwrap(); + graph.add_edge(0, 3, 0, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let partition = Partition::from_iter([0usize, 0, 1, 1]); + let mut m = ModularityUnDir::new(graph, None, 1.0, partition, 1e-8); + let value_before = m.value(); + let _ = m.aggregate(); + let value_after = m.value(); + info!("before: {value_before}, after: {value_after}"); + assert_eq!(value_after, value_before); + let delta = m.move_delta(&VID(0), ComID(1)); + m.move_node(&VID(0), ComID(1)); + let value_merged = m.value(); + assert_eq!(value_merged, 0.0); + assert!((value_merged - (value_after + delta)).abs() < 1e-8); + }); +} diff --git a/raphtory-test-utils/tests/algo_tests/components.rs b/raphtory-test-utils/tests/algo_tests/components.rs new file mode 100644 index 0000000000..61000fba7a --- /dev/null +++ b/raphtory-test-utils/tests/algo_tests/components.rs @@ -0,0 +1,949 @@ +use ahash::HashSet; +use proptest::{prelude::Strategy, proptest, sample::Index}; +use raphtory::{ + algorithms::components::{weakly_connected_components, ConnectedComponent}, + db::api::{ + mutation::AdditionOps, + state::{NodeStateValue, TypedNodeState}, + view::internal::GraphView, + }, + prelude::*, +}; +use raphtory_test_utils::test_storage; +use serde::{Deserialize, Serialize}; +use std::{ + collections::{BTreeSet, HashMap}, + fmt::{Debug, Formatter}, + hash::Hash, +}; + +fn assert_same_partition< + V: NodeStateValue + Hash + Eq + 'static, + G: GraphView + 'static, + ID: Into, +>( + left: TypedNodeState<'static, V, G>, // NodeState, + right: impl IntoIterator>, +) { + let left_groups: HashSet> = left + .groups() + .into_iter_groups() + .map(|(_, nodes)| nodes.id().collect()) + .collect(); + let right_groups: HashSet> = right + .into_iter() + .map(|inner| inner.into_iter().map(|id| id.into()).collect()) + .collect(); + assert_eq!(left_groups, right_groups); +} + +#[test] +fn run_loop_simple_connected_components() { + let graph = Graph::new(); + + let edges = vec![ + (1, 2, 1), + (2, 3, 2), + (3, 4, 3), + (3, 5, 4), + (6, 5, 5), + (7, 8, 6), + (8, 7, 7), + ]; + + for (src, dst, ts) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + for _ in 0..1000 { + let results = weakly_connected_components(graph); + assert_same_partition(results, [1..=6, 7..=8]); + } + }); +} + +#[test] +fn simple_connected_components_2() { + let graph = Graph::new(); + + let edges = vec![ + (1, 2, 1), + (1, 3, 2), + (1, 4, 3), + (3, 1, 4), + (3, 4, 5), + (3, 5, 6), + (4, 5, 7), + (5, 6, 8), + (5, 8, 9), + (7, 5, 10), + (8, 5, 11), + (1, 9, 12), + (9, 1, 13), + (6, 3, 14), + (4, 8, 15), + (8, 3, 16), + (5, 10, 17), + (10, 5, 18), + (10, 8, 19), + (1, 11, 20), + (11, 1, 21), + (9, 11, 22), + (11, 9, 23), + ]; + + for (src, dst, ts) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let results = weakly_connected_components(graph); + assert_same_partition(results, [1..=11]); + }); +} + +#[test] +fn test_multiple_components() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (2, 2, 1), + (3, 3, 1), + (1, 10, 11), + (2, 20, 21), + (3, 30, 31), + ]; + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + for _ in 0..1000 { + let result = weakly_connected_components(&graph); + assert_same_partition( + result, + [vec![1, 2, 3], vec![10, 11], vec![20, 21], vec![30, 31]], + ) + } +} + +// connected community_detection on a graph with 1 node and a self loop +#[test] +fn simple_connected_components_3() { + let graph = Graph::new(); + + let edges = vec![(1, 1, 1)]; + + for (src, dst, ts) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + for _ in 0..1000 { + // loop to test for weird non-deterministic behaviour + let results = weakly_connected_components(graph); + assert_same_partition(results, [[1]]); + } + }); +} + +#[test] +fn windowed_connected_components() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).expect("add edge"); + graph.add_edge(0, 2, 1, NO_PROPS, None).expect("add edge"); + graph.add_edge(9, 3, 4, NO_PROPS, None).expect("add edge"); + graph.add_edge(9, 4, 3, NO_PROPS, None).expect("add edge"); + + test_storage!(&graph, |graph| { + let results = weakly_connected_components(graph); + assert_same_partition(results, [[1, 2], [3, 4]]); + + let wg = graph.window(0, 2); + let results = weakly_connected_components(&wg); + assert_same_partition(results, [[1, 2]]); + }); +} + +#[test] +fn layered_connected_components() { + let g = Graph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("ZERO-TWO")).unwrap(); + g.add_edge(1, 1, 3, NO_PROPS, Some("ZERO-TWO")).unwrap(); + g.add_edge(2, 4, 5, NO_PROPS, Some("ZERO-TWO")).unwrap(); + g.add_edge(3, 6, 7, NO_PROPS, Some("THREE-FIVE")).unwrap(); + g.add_edge(4, 8, 9, NO_PROPS, Some("THREE-FIVE")).unwrap(); + + let g_layer_zero_two = g.layers("ZERO-TWO").unwrap(); + + assert_eq!(g_layer_zero_two.nodes().id(), [1, 2, 3, 4, 5]); + let g_layer_three_five = g.layers("THREE-FIVE").unwrap(); + + let res_zero_two = weakly_connected_components(&g_layer_zero_two); + let c1 = res_zero_two.get_by_node(1).unwrap(); + let c2 = res_zero_two.get_by_node(4).unwrap(); + + let expected_zero_two: HashMap = + [(1, c1), (2, c1), (3, c1), (4, c2), (5, c2)].into(); + + assert_eq!(res_zero_two, expected_zero_two); + + let res_three_five = weakly_connected_components(&g_layer_three_five); + + let c6 = res_three_five.get_by_node(6).unwrap().component_id; + let c7 = res_three_five.get_by_node(8).unwrap().component_id; + + let expected_three_five: HashMap = [(6u64, c6), (7, c6), (8, c7), (9, c7)].into(); + assert_eq!(res_three_five, expected_three_five); +} + +#[derive(Serialize, Deserialize)] +struct ComponentTestInput { + edges: Vec<(u64, u64)>, + components: Vec>, +} + +impl Debug for ComponentTestInput { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(&serde_json::to_string(self).unwrap()) + } +} + +fn random_component_edges( + num_components: usize, + num_nodes_per_component: usize, +) -> impl Strategy { + let vs = proptest::collection::vec( + proptest::collection::vec( + ( + 0..num_nodes_per_component, + proptest::arbitrary::any::(), + ), + 2..=num_nodes_per_component, + ), + 0..=num_components, + ); + vs.prop_map(move |vs| { + let mut edges = Vec::new(); + let mut components = Vec::new(); + for (ci, c) in vs.into_iter().enumerate() { + let offset = num_nodes_per_component * ci; + let component: Vec<_> = c.iter().map(|(i, _)| (*i + offset) as u64).collect(); + for i in 1..c.len() { + let n = component[c[i].1.index(i)]; + edges.push((component[i], n)); + } + components.push(component.into_iter().collect()); + } + ComponentTestInput { edges, components } + }) +} + +#[test] +fn weakly_connected_components_proptest() { + proptest!(|(input in random_component_edges(10, 100))|{ + let ComponentTestInput {edges, components } = input; + let g = Graph::new(); + for (src, dst) in edges { + g.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + for _ in 0..10 { + let result = weakly_connected_components(&g); + assert_same_partition(result, &components); + } + }) +} + +mod in_component_test { + use itertools::Itertools; + use raphtory::{ + algorithms::components::{ + in_component, in_component_filtered, in_components, in_components_filtered, + }, + db::{ + api::mutation::AdditionOps, + graph::views::filter::{ + model::{ + graph_filter::GraphFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, TryAsCompositeFilter, ViewWrapOps, + }, + CreateFilter, + }, + }, + prelude::*, + }; + use raphtory_test_utils::test_storage; + use std::collections::HashMap; + + fn check_node(graph: &Graph, node_id: u64, mut correct: Vec<(u64, usize)>) { + let mut results: Vec<_> = in_component(graph.node(node_id).unwrap()) + .iter() + .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) + .collect(); + results.sort(); + correct.sort(); + assert_eq!(results, correct); + } + + fn check_node_filtered( + graph: &Graph, + node_id: u64, + filter: F, + mut correct: Vec<(u64, usize)>, + ) { + let mut results: Vec<_> = in_component_filtered(graph.node(node_id).unwrap(), filter) + .unwrap() + .iter() + .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) + .collect(); + + results.sort(); + correct.sort(); + assert_eq!(results, correct); + } + + #[test] + fn in_component_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 1, 3), + (1, 2, 4), + (1, 2, 5), + (1, 5, 4), + (1, 4, 6), + (1, 4, 7), + (1, 5, 8), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + + check_node(&graph, 1, vec![]); + check_node(&graph, 2, vec![(1, 1)]); + check_node(&graph, 3, vec![(1, 1)]); + check_node(&graph, 4, vec![(1, 2), (2, 1), (5, 1)]); + check_node(&graph, 5, vec![(1, 2), (2, 1)]); + check_node(&graph, 6, vec![(1, 3), (2, 2), (4, 1), (5, 2)]); + check_node(&graph, 7, vec![(1, 3), (2, 2), (4, 1), (5, 2)]); + check_node(&graph, 8, vec![(1, 3), (2, 2), (5, 1)]); + } + + #[test] + fn test_distances() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); + graph.add_edge(0, 5, 3, NO_PROPS, None).unwrap(); + + check_node(&graph, 3, vec![(1, 2), (2, 1), (4, 2), (5, 1)]); + } + + #[test] + fn in_components_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 1, 3), + (1, 2, 4), + (1, 2, 5), + (1, 5, 4), + (1, 4, 6), + (1, 4, 7), + (1, 5, 8), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let results = in_components(graph, None); + let mut correct = HashMap::new(); + correct.insert("1".to_string(), vec![]); + correct.insert("2".to_string(), vec![1]); + correct.insert("3".to_string(), vec![1]); + correct.insert("4".to_string(), vec![1, 2, 5]); + correct.insert("5".to_string(), vec![1, 2]); + correct.insert("6".to_string(), vec![1, 2, 4, 5]); + correct.insert("7".to_string(), vec![1, 2, 4, 5]); + correct.insert("8".to_string(), vec![1, 2, 5]); + let map: HashMap> = results + .into_iter() + .map(|(k, v)| { + ( + k.name(), + v.in_components + .into_iter() + .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) + .sorted() + .collect(), + ) + }) + .collect(); + assert_eq!(map, correct); + }); + } + + #[test] + fn in_component_filtered_by_layer_removes_cross_layer_ancestors() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); + + graph.add_edge(1, 2, 5, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 5, 4, NO_PROPS, Some("B")).unwrap(); + + // Sanity: with A-only filter, 5 should disappear + let filter = GraphFilter.layer("A"); + + check_node_filtered(&graph, 6, filter.clone(), vec![(4, 1), (2, 2), (1, 3)]); + check_node_filtered(&graph, 4, filter.clone(), vec![(2, 1), (1, 2)]); + check_node_filtered(&graph, 5, filter.clone(), vec![]); // B-only inbound edges, so none under A + } + + #[test] + fn in_components_filtered_by_layer_matches_expected_node_sets() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); + + graph.add_edge(1, 2, 5, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 5, 4, NO_PROPS, Some("B")).unwrap(); + + test_storage!(&graph, |graph| { + let results = in_components_filtered(graph, None, GraphFilter.layer("A")).unwrap(); + + let mut correct: HashMap> = HashMap::new(); + correct.insert("1".to_string(), vec![]); + correct.insert("2".to_string(), vec![1]); + correct.insert("4".to_string(), vec![1, 2]); + correct.insert("5".to_string(), vec![]); // only reachable via B edges, which are filtered out + correct.insert("6".to_string(), vec![1, 2, 4]); + + let map: HashMap> = results + .into_iter() + .map(|(k, v)| { + ( + k.name(), + v.in_components + .into_iter() + .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) + .sorted() + .collect(), + ) + }) + .collect(); + + assert_eq!(map, correct); + }); + } + + #[test] + fn in_component_filtered_by_layer_handles_multiple_inbound_paths_with_distances() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + + // Layer B adds an alternate chain to 4 that should be ignored under A filter + graph.add_edge(1, 10, 11, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 11, 4, NO_PROPS, Some("B")).unwrap(); + + check_node_filtered( + &graph, + 6, + GraphFilter.layer("A"), + vec![(4, 1), (2, 2), (3, 2), (1, 3)], + ); + } + + #[test] + fn in_component_filtered_returns_nodes_that_are_unfiltered_for_future_traversals() { + let graph = Graph::new(); + + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 99, 2, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 100, 99, NO_PROPS, Some("B")).unwrap(); + + let result = in_component_filtered(graph.node(4).unwrap(), GraphFilter.layer("A")).unwrap(); + let result = result + .iter() + .flat_map(|(n, _)| n.id().as_u64()) + .collect::>(); + assert_eq!(result, vec![2]); + let unfiltered_ids: Vec = + in_component_filtered(graph.node(4).unwrap(), GraphFilter.layer("A")) + .unwrap() + .nodes() + .in_neighbours() + .iter() + .flat_map(|(_, ns)| ns.iter().filter_map(|c| c.id().as_u64())) + .collect(); + + assert_eq!(unfiltered_ids, vec![99]); + } + + #[test] + fn in_component_edge_filtered_handles_multiple_inbound_paths_with_distances() { + let graph = Graph::new(); + + graph + .add_edge(1, 1, 2, vec![("p1", Prop::U64(1))], Some("A")) + .unwrap(); + graph + .add_edge(1, 2, 4, vec![("p1", Prop::U64(2))], Some("A")) + .unwrap(); + graph + .add_edge(1, 4, 6, vec![("p1", Prop::U64(3))], Some("A")) + .unwrap(); + graph + .add_edge(1, 3, 4, vec![("p1", Prop::U64(4))], Some("A")) + .unwrap(); + + // Layer B adds an alternate chain to 4 that should be ignored under A filter + graph + .add_edge(1, 10, 11, vec![("p1", Prop::U64(2))], Some("B")) + .unwrap(); + graph + .add_edge(1, 11, 4, vec![("p1", Prop::U64(2))], Some("B")) + .unwrap(); + + let filter = EdgeFilter.layer("A").property("p1").ge(3u64); + check_node_filtered(&graph, 6, filter, vec![(3, 2), (4, 1)]); + } +} + +#[cfg(test)] +mod components_test { + use itertools::Itertools; + use raphtory::{ + algorithms::components::{ + out_component, out_component_filtered, out_components, out_components_filtered, + }, + db::{ + api::mutation::AdditionOps, + graph::views::filter::{ + model::{ + graph_filter::GraphFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, ViewWrapOps, + }, + CreateFilter, + }, + }, + prelude::*, + }; + use raphtory_test_utils::test_storage; + use std::collections::HashMap; + + fn check_node(graph: &Graph, node_id: u64, mut correct: Vec<(u64, usize)>) { + let mut results: Vec<_> = out_component(graph.node(node_id).unwrap()) + .iter() + .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) + .collect(); + results.sort(); + correct.sort(); + assert_eq!(results, correct); + } + + fn check_node_filtered( + graph: &Graph, + node_id: u64, + filter: F, + mut correct: Vec<(u64, usize)>, + ) { + let mut results: Vec<_> = out_component_filtered(graph.node(node_id).unwrap(), filter) + .unwrap() + .iter() + .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) + .collect(); + results.sort(); + correct.sort(); + assert_eq!(results, correct); + } + + #[test] + fn out_component_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 1, 3), + (1, 2, 3), + (1, 2, 4), + (1, 2, 5), + (1, 5, 4), + (1, 4, 6), + (1, 4, 7), + (1, 5, 8), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + + check_node( + &graph, + 1, + vec![(2, 1), (3, 1), (4, 2), (5, 2), (6, 3), (7, 3), (8, 3)], + ); + check_node( + &graph, + 2, + vec![(3, 1), (4, 1), (5, 1), (6, 2), (7, 2), (8, 2)], + ); + check_node(&graph, 3, vec![]); + check_node(&graph, 4, vec![(6, 1), (7, 1)]); + check_node(&graph, 5, vec![(4, 1), (6, 2), (7, 2), (8, 1)]); + check_node(&graph, 6, vec![]); + check_node(&graph, 7, vec![]); + check_node(&graph, 8, vec![]); + } + + #[test] + fn test_distances() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); + graph.add_edge(0, 5, 3, NO_PROPS, None).unwrap(); + + check_node(&graph, 1, vec![(2, 1), (3, 2), (4, 1), (5, 2)]); + } + + #[test] + fn out_components_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 1, 3), + (1, 2, 4), + (1, 2, 5), + (1, 5, 4), + (1, 4, 6), + (1, 4, 7), + (1, 5, 8), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let results = out_components(graph, None); + + let mut correct = HashMap::new(); + correct.insert("1".to_string(), vec![2, 3, 4, 5, 6, 7, 8]); + correct.insert("2".to_string(), vec![4, 5, 6, 7, 8]); + correct.insert("3".to_string(), vec![]); + correct.insert("4".to_string(), vec![6, 7]); + correct.insert("5".to_string(), vec![4, 6, 7, 8]); + correct.insert("6".to_string(), vec![]); + correct.insert("7".to_string(), vec![]); + correct.insert("8".to_string(), vec![]); + let map: HashMap> = results + .iter() + .map(|(k, v)| { + ( + k.name(), + v.out_components + .into_iter() + .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) + .sorted() + .collect(), + ) + }) + .collect(); + assert_eq!(map, correct); + }); + } + + #[test] + fn out_component_filtered_by_layer_prunes_cross_layer_paths() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 3, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + + graph.add_edge(1, 2, 10, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 10, 11, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 11, 4, NO_PROPS, Some("B")).unwrap(); + + let filter = GraphFilter.layer("A"); + + check_node_filtered(&graph, 1, filter.clone(), vec![(2, 1), (3, 2), (4, 3)]); + + check_node_filtered(&graph, 2, filter.clone(), vec![(3, 1), (4, 2)]); + + check_node_filtered(&graph, 10, filter.clone(), vec![]); + check_node_filtered(&graph, 11, filter.clone(), vec![]); + } + + #[test] + fn out_component_filtered_by_layer_distances_follow_filtered_graph_only() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 1, 3, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + + graph.add_edge(1, 1, 99, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); + + let filter = GraphFilter.layer("A"); + + check_node_filtered(&graph, 1, filter, vec![(2, 1), (3, 1), (4, 2)]); + } + + #[test] + fn out_components_filtered_by_layer_matches_expected_node_sets() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 3, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 5, NO_PROPS, Some("A")).unwrap(); + + graph.add_edge(1, 5, 100, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 1, 200, NO_PROPS, Some("B")).unwrap(); + + test_storage!(&graph, |graph| { + let results = out_components_filtered(graph, None, GraphFilter.layer("A")).unwrap(); + + let mut correct: HashMap> = HashMap::new(); + correct.insert("1".to_string(), vec![2, 3, 4, 5]); + correct.insert("2".to_string(), vec![3, 4, 5]); + correct.insert("3".to_string(), vec![4]); + correct.insert("4".to_string(), vec![]); + correct.insert("5".to_string(), vec![]); + correct.insert("100".to_string(), vec![]); + correct.insert("200".to_string(), vec![]); + + let map: HashMap> = results + .into_iter() + .map(|(k, v)| { + ( + k.name(), + v.out_components + .into_iter() + .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) + .sorted() + .collect(), + ) + }) + .collect(); + + assert_eq!(map, correct); + }); + } + + #[test] + fn out_component_filtered_returns_nodes_that_are_unfiltered_for_future_traversals() { + let graph = Graph::new(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 99, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); + + let unfiltered_ids: Vec = + out_component_filtered(graph.node(1).unwrap(), GraphFilter.layer("A")) + .unwrap() + .nodes() + .out_neighbours() + .iter() + .flat_map(|(_, ns)| ns.iter().filter_map(|c| c.id().as_u64())) + .collect(); + + assert_eq!(unfiltered_ids, vec![99]); + } + + #[test] + fn out_component_node_filtered_distances_follow_filtered_graph_only() { + let graph = Graph::new(); + + graph + .add_node(1, 1, vec![("p1", Prop::U64(1))], None, None) + .unwrap(); + graph + .add_node(1, 2, vec![("p1", Prop::U64(2))], None, None) + .unwrap(); + graph + .add_node(1, 3, vec![("p1", Prop::U64(3))], None, None) + .unwrap(); + graph + .add_node(1, 4, vec![("p1", Prop::U64(4))], None, None) + .unwrap(); + + graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 1, 3, NO_PROPS, Some("A")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); + + graph.add_edge(1, 1, 99, NO_PROPS, Some("B")).unwrap(); + graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); + + let filter = NodeFilter.property("p1").ge(3u64); + + check_node_filtered(&graph, 1, filter, vec![(3, 1), (4, 2)]); + } +} + +#[cfg(test)] +mod strongly_connected_components_tests { + use itertools::Itertools; + use raphtory::{ + algorithms::components::strongly_connected_components, + prelude::{AdditionOps, Graph, NodeStateGroupBy, NodeStateOps, NodeViewOps, NO_PROPS}, + }; + use raphtory_test_utils::test_storage; + use std::collections::HashSet; + + #[test] + fn scc_test() { + let graph = Graph::new(); + let edges = vec![ + (1, 1, 2), + (1, 2, 3), + (1, 2, 5), + (1, 3, 4), + (1, 5, 6), + (1, 6, 4), + (1, 6, 7), + (1, 7, 8), + (1, 8, 6), + (1, 6, 2), + ]; + + for (ts, src, dst) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let scc_nodes: HashSet> = strongly_connected_components(graph) + .groups() + .into_iter_groups() + .map(|(_, v)| v.name().into_iter_values().sorted().collect()) + .collect(); + + let expected: HashSet> = [ + vec!["2", "5", "6", "7", "8"], + vec!["1"], + vec!["3"], + vec!["4"], + ] + .into_iter() + .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) + .collect(); + assert_eq!(scc_nodes, expected); + }); + } + + #[test] + fn scc_test_multiple_components() { + let graph = Graph::new(); + let edges = [ + (1, 2), + (2, 3), + (2, 8), + (3, 4), + (3, 7), + (4, 5), + (5, 3), + (5, 6), + (7, 4), + (7, 6), + (8, 1), + (8, 7), + ]; + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let scc_nodes: HashSet> = strongly_connected_components(graph) + .groups() + .into_iter_groups() + .map(|(_, v)| v.name().into_iter_values().sorted().collect()) + .collect(); + + let expected: HashSet> = + [vec!["3", "4", "5", "7"], vec!["1", "2", "8"], vec!["6"]] + .into_iter() + .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) + .collect(); + assert_eq!(scc_nodes, expected); + }); + } + + #[test] + fn scc_test_multiple_components_2() { + let graph = Graph::new(); + let edges = [(1, 2), (1, 3), (1, 4), (4, 2), (3, 4), (2, 3)]; + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let scc_nodes: HashSet> = strongly_connected_components(graph) + .groups() + .into_iter_groups() + .map(|(_, v)| v.name().into_iter_values().sorted().collect()) + .collect(); + + let expected: HashSet> = [vec!["2", "3", "4"], vec!["1"]] + .into_iter() + .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) + .collect(); + assert_eq!(scc_nodes, expected); + }); + } + + #[test] + fn scc_test_all_singletons() { + let graph = Graph::new(); + let edges = [ + (0, 1), + (1, 2), + (1, 3), + (2, 4), + (2, 5), + (3, 4), + (3, 5), + (4, 6), + ]; + for (src, dst) in edges { + graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let scc_nodes: HashSet> = strongly_connected_components(graph) + .groups() + .into_iter_groups() + .map(|(_, v)| v.name().into_iter_values().sorted().collect()) + .collect(); + + let expected: HashSet> = [ + vec!["0"], + vec!["1"], + vec!["2"], + vec!["3"], + vec!["4"], + vec!["5"], + vec!["6"], + ] + .into_iter() + .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) + .collect(); + assert_eq!(scc_nodes, expected); + }); + } +} diff --git a/raphtory/tests/algo_tests/cores.rs b/raphtory-test-utils/tests/algo_tests/cores.rs similarity index 95% rename from raphtory/tests/algo_tests/cores.rs rename to raphtory-test-utils/tests/algo_tests/cores.rs index cdf74a7e95..8df54a10f3 100644 --- a/raphtory/tests/algo_tests/cores.rs +++ b/raphtory-test-utils/tests/algo_tests/cores.rs @@ -1,6 +1,6 @@ -#[cfg(all(test, feature = "test-utils"))] mod k_core_test { - use raphtory::{algorithms::cores::k_core::k_core_set, prelude::*, test_storage}; + use raphtory::{algorithms::cores::k_core::k_core_set, prelude::*}; + use raphtory_test_utils::test_storage; use std::collections::HashSet; #[test] diff --git a/raphtory/tests/algo_tests/embeddings.rs b/raphtory-test-utils/tests/algo_tests/embeddings.rs similarity index 99% rename from raphtory/tests/algo_tests/embeddings.rs rename to raphtory-test-utils/tests/algo_tests/embeddings.rs index 26cdea70c4..41de4db5ee 100644 --- a/raphtory/tests/algo_tests/embeddings.rs +++ b/raphtory-test-utils/tests/algo_tests/embeddings.rs @@ -1,10 +1,9 @@ -#[cfg(all(test, feature = "test-utils"))] mod fast_rp_test { use raphtory::{ algorithms::embeddings::fast_rp::fast_rp, db::api::mutation::AdditionOps, prelude::*, - test_storage, }; + use raphtory_test_utils::test_storage; use std::collections::HashMap; #[test] diff --git a/raphtory/tests/algo_tests/metrics.rs b/raphtory-test-utils/tests/algo_tests/metrics.rs similarity index 95% rename from raphtory/tests/algo_tests/metrics.rs rename to raphtory-test-utils/tests/algo_tests/metrics.rs index 96535adeba..086b5b51bc 100644 --- a/raphtory/tests/algo_tests/metrics.rs +++ b/raphtory-test-utils/tests/algo_tests/metrics.rs @@ -1,12 +1,10 @@ -#[cfg(all(test, feature = "test-utils"))] mod cc_test { - use pretty_assertions::assert_eq; use raphtory::{ algorithms::metrics::clustering_coefficient::global_clustering_coefficient::global_clustering_coefficient, db::{api::mutation::AdditionOps, graph::graph::Graph}, prelude::NO_PROPS, - test_storage, }; + use raphtory_test_utils::test_storage; /// Test the global clustering coefficient #[test] @@ -48,7 +46,6 @@ mod cc_test { } } -#[cfg(all(test, feature = "test-utils"))] mod clustering_coefficient_tests { use raphtory::{ algorithms::metrics::clustering_coefficient::{ @@ -60,8 +57,8 @@ mod clustering_coefficient_tests { graph::graph::Graph, }, prelude::NO_PROPS, - test_storage, }; + use raphtory_test_utils::test_storage; use std::collections::HashMap; #[test] @@ -121,15 +118,13 @@ mod clustering_coefficient_tests { } } -#[cfg(all(test, feature = "test-utils"))] mod sum_weight_test { - use pretty_assertions::assert_eq; use raphtory::{ algorithms::metrics::balance::balance, db::{api::mutation::AdditionOps, graph::graph::Graph}, - test_storage, }; use raphtory_api::core::{entities::properties::prop::Prop, Direction}; + use raphtory_test_utils::test_storage; use std::collections::HashMap; #[test] @@ -203,7 +198,6 @@ mod sum_weight_test { } } -#[cfg(all(test, feature = "test-utils"))] mod degree_test { use raphtory::{ algorithms::metrics::degree::{ @@ -212,8 +206,8 @@ mod degree_test { }, db::{api::mutation::AdditionOps, graph::graph::Graph}, prelude::NO_PROPS, - test_storage, }; + use raphtory_test_utils::test_storage; #[test] fn degree_test() { @@ -264,14 +258,13 @@ mod degree_test { } } -#[cfg(all(test, feature = "test-utils"))] mod directed_graph_density_tests { use raphtory::{ algorithms::metrics::directed_graph_density::directed_graph_density, db::{api::mutation::AdditionOps, graph::graph::Graph}, prelude::*, - test_storage, }; + use raphtory_test_utils::test_storage; #[test] fn low_graph_density() { @@ -319,16 +312,14 @@ mod directed_graph_density_tests { } } -#[cfg(all(test, feature = "test-utils"))] mod reciprocity_test { - use pretty_assertions::assert_eq; use raphtory::{ algorithms::metrics::reciprocity::{all_local_reciprocity, global_reciprocity}, prelude::*, - test_storage, }; use std::collections::HashMap; + use raphtory_test_utils::test_storage; #[test] fn test_global_recip() { let graph = Graph::new(); diff --git a/raphtory/tests/algo_tests/mod.rs b/raphtory-test-utils/tests/algo_tests/mod.rs similarity index 88% rename from raphtory/tests/algo_tests/mod.rs rename to raphtory-test-utils/tests/algo_tests/mod.rs index 8d3bb8812d..bc098b5314 100644 --- a/raphtory/tests/algo_tests/mod.rs +++ b/raphtory-test-utils/tests/algo_tests/mod.rs @@ -1,12 +1,20 @@ use std::collections::HashMap; +#[cfg(test)] mod centrality; +#[cfg(test)] mod community_detection; +#[cfg(test)] mod components; +#[cfg(test)] mod cores; +#[cfg(test)] mod embeddings; +#[cfg(test)] mod metrics; +#[cfg(test)] mod motifs; +#[cfg(test)] mod pathing; fn assert_eq_f64(a: f64, b: f64, precision: f64) { diff --git a/raphtory/tests/algo_tests/motifs.rs b/raphtory-test-utils/tests/algo_tests/motifs.rs similarity index 97% rename from raphtory/tests/algo_tests/motifs.rs rename to raphtory-test-utils/tests/algo_tests/motifs.rs index f18de8c9fd..3fcd6df8c0 100644 --- a/raphtory/tests/algo_tests/motifs.rs +++ b/raphtory-test-utils/tests/algo_tests/motifs.rs @@ -1,11 +1,10 @@ -#[cfg(all(test, feature = "test-utils"))] mod global_motifs_test { use raphtory::{ algorithms::motifs::global_temporal_three_node_motifs::temporal_three_node_motif_multi, db::{api::mutation::AdditionOps, graph::graph::Graph}, prelude::*, - test_storage, }; + use raphtory_test_utils::test_storage; fn load_graph(edges: Vec<(i64, u64, u64)>) -> Graph { let graph = Graph::new(); @@ -66,15 +65,14 @@ mod global_motifs_test { } } -#[cfg(all(test, feature = "test-utils"))] mod local_motifs_test { use raphtory::{ algorithms::motifs::local_temporal_three_node_motifs::temporal_three_node_motif, db::{api::mutation::AdditionOps, graph::graph::Graph}, prelude::*, - test_storage, }; use raphtory_api::core::utils::logging::global_debug_logger; + use raphtory_test_utils::test_storage; use std::collections::HashMap; use tracing::info; @@ -376,7 +374,6 @@ mod local_motifs_test { } } -#[cfg(all(test, feature = "test-utils"))] mod triangle_count_tests { use raphtory::{ algorithms::motifs::local_triangle_count::local_triangle_count, @@ -385,8 +382,8 @@ mod triangle_count_tests { graph::graph::Graph, }, prelude::*, - test_storage, }; + use raphtory_test_utils::test_storage; #[test] fn counts_triangles() { @@ -409,7 +406,6 @@ mod triangle_count_tests { } } -#[cfg(all(test, feature = "test-utils"))] mod rich_club_test { use crate::algo_tests::assert_eq_f64; use raphtory::{ @@ -475,14 +471,13 @@ mod rich_club_test { } } -#[cfg(all(test, feature = "test-utils"))] mod triangle_count_tests_alt { use raphtory::{ algorithms::motifs::triangle_count::triangle_count, db::{api::mutation::AdditionOps, graph::graph::Graph}, prelude::NO_PROPS, - test_storage, }; + use raphtory_test_utils::test_storage; use std::time::Instant; #[ignore] @@ -575,15 +570,13 @@ mod triangle_count_tests_alt { } } -#[cfg(all(test, feature = "test-utils"))] mod triplet_test { - use pretty_assertions::assert_eq; use raphtory::{ algorithms::motifs::triplet_count::triplet_count, db::{api::mutation::AdditionOps, graph::graph::Graph}, prelude::*, - test_storage, }; + use raphtory_test_utils::test_storage; /// Test the global clustering coefficient #[test] diff --git a/raphtory/tests/algo_tests/pathing.rs b/raphtory-test-utils/tests/algo_tests/pathing.rs similarity index 98% rename from raphtory/tests/algo_tests/pathing.rs rename to raphtory-test-utils/tests/algo_tests/pathing.rs index c9c7720a67..6cd9e8ab54 100644 --- a/raphtory/tests/algo_tests/pathing.rs +++ b/raphtory-test-utils/tests/algo_tests/pathing.rs @@ -1,14 +1,13 @@ -#[cfg(all(test, feature = "test-utils"))] mod dijkstra_tests { use itertools::Itertools; use raphtory::{ algorithms::pathing::dijkstra::dijkstra_single_source_shortest_paths, db::{api::mutation::AdditionOps, graph::graph::Graph}, prelude::*, - test_storage, }; use raphtory_api::core::Direction; + use raphtory_test_utils::test_storage; fn load_graph(edges: Vec<(i64, &str, &str, Vec<(&str, f32)>)>) -> Graph { let graph = Graph::new(); @@ -425,16 +424,15 @@ mod dijkstra_tests { } } -#[cfg(all(test, feature = "test-utils"))] mod sssp_tests { use itertools::Itertools; use raphtory::{ algorithms::pathing::single_source_shortest_path::single_source_shortest_path, db::{api::mutation::AdditionOps, graph::graph::Graph}, prelude::*, - test_storage, }; use raphtory_api::core::utils::logging::global_info_logger; + use raphtory_test_utils::test_storage; use std::collections::HashMap; fn load_graph(edges: Vec<(i64, u64, u64)>) -> Graph { @@ -499,19 +497,18 @@ mod sssp_tests { } } -#[cfg(all(test, feature = "test-utils"))] mod generic_taint_tests { use raphtory::{ algorithms::pathing::temporal_reachability::temporally_reachable_nodes, + core::entities::nodes::node_ref::AsNodeRef, db::{ api::{mutation::AdditionOps, view::StaticGraphViewOps}, graph::graph::Graph, }, prelude::*, - test_storage, }; - use raphtory_core::entities::nodes::node_ref::AsNodeRef; + use raphtory_test_utils::test_storage; use std::collections::HashMap; fn sort_inner_by_string( diff --git a/raphtory/tests/algorithms.rs b/raphtory-test-utils/tests/algorithms.rs similarity index 100% rename from raphtory/tests/algorithms.rs rename to raphtory-test-utils/tests/algorithms.rs diff --git a/raphtory-test-utils/tests/cached_view.rs b/raphtory-test-utils/tests/cached_view.rs new file mode 100644 index 0000000000..9293595aa5 --- /dev/null +++ b/raphtory-test-utils/tests/cached_view.rs @@ -0,0 +1,370 @@ +use itertools::Itertools; +use proptest::prelude::*; +use raphtory::{ + algorithms::motifs::triangle_count::triangle_count, db::graph::graph::assert_graph_equal, + prelude::*, +}; +use raphtory_api::core::storage::timeindex::AsTime; +use raphtory_test_utils::test_storage; + +#[test] +fn empty_graph() { + let graph = Graph::new(); + test_storage!(&graph, |graph| { + let sg = graph.cache_view(); + assert_graph_equal(&sg, &graph); + }); +} + +#[test] +fn empty_window() { + let graph = Graph::new(); + graph.add_edge(1, 1, 1, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + let window = graph.window(2, 3); + let sg = window.cache_view(); + assert_graph_equal(&window, &sg); + }); +} + +#[test] +fn test_materialize_no_edges() { + let graph = Graph::new(); + + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 2, NO_PROPS, None, None).unwrap(); + + test_storage!(&graph, |graph| { + let sg = graph.cache_view(); + + let actual = sg.materialize().unwrap().into_events().unwrap(); + assert_graph_equal(&actual, &sg); + }); +} + +#[test] +fn test_mask_the_window_50pc() { + let graph = Graph::new(); + let edges = vec![ + (1, 2, 1), + (1, 3, 2), + (1, 4, 3), + (3, 1, 4), + (3, 4, 5), + (3, 5, 6), + (4, 5, 7), + (5, 6, 8), + (5, 8, 9), + (7, 5, 10), + (8, 5, 11), + (1, 9, 12), + (9, 1, 13), + (6, 3, 14), + (4, 8, 15), + (8, 3, 16), + (5, 10, 17), + (10, 5, 18), + (10, 8, 19), + (1, 11, 20), + (11, 1, 21), + (9, 11, 22), + (11, 9, 23), + ]; + for (src, dst, ts) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let window = graph.window(12, 24); + let mask = window.cache_view(); + let ts = triangle_count(&mask, None); + let tg = triangle_count(&window, None); + assert_eq!(ts, tg); + }); +} + +#[test] +fn masked_always_equals() { + fn check(edge_list: &[(u8, u8, i16, u8)]) { + let graph = Graph::new(); + for (src, dst, ts, layer) in edge_list { + graph + .add_edge( + *ts as i64, + *src as u64, + *dst as u64, + NO_PROPS, + Some(&layer.to_string()), + ) + .unwrap(); + } + + test_storage!(&graph, |graph| { + let layers = graph + .unique_layers() + .take(graph.unique_layers().count() / 2) + .collect_vec(); + + let earliest = graph.earliest_time().unwrap().t(); + let latest = graph.latest_time().unwrap().t(); + let middle = earliest + (latest - earliest) / 2; + + if !layers.is_empty() && earliest < middle && middle < latest { + let subgraph = graph.layers(layers).unwrap().window(earliest, middle); + let masked = subgraph.cache_view(); + assert_graph_equal(&subgraph, &masked); + } + }); + } + + proptest!(|(edge_list in any::>().prop_filter("greater than 3",|v| !v.is_empty() ))| { + check(&edge_list); + }) +} + +#[cfg(test)] +mod test_filters_cached_view { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::{cached_view::CachedView, window_graph::WindowedGraph}, + }, + prelude::{GraphViewOps, TimeOps}, + }; + use raphtory_test_utils::assertions::GraphTransformer; + use std::ops::Range; + + struct CachedGraphTransformer; + + impl GraphTransformer for CachedGraphTransformer { + type Return = CachedView; + fn apply(&self, graph: G) -> Self::Return { + graph.cache_view() + } + } + + struct WindowedCachedGraphTransformer(Range); + + impl GraphTransformer for WindowedCachedGraphTransformer { + type Return = WindowedGraph>; + fn apply(&self, graph: G) -> Self::Return { + graph.cache_view().window(self.0.start, self.0.end) + } + } + + mod test_nodes_filters_cached_view_graph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::{ + node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, + }, + }, + prelude::AdditionOps, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_test_utils::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, + TestVariants, + }; + + use crate::test_filters_cached_view::{ + CachedGraphTransformer, WindowedCachedGraphTransformer, + }; + + fn init_graph(graph: G) -> G { + let node_data = vec![ + (6, "N1", 2u64, "air_nomad"), + (7, "N1", 1u64, "air_nomad"), + (6, "N2", 1u64, "water_tribe"), + (7, "N2", 2u64, "water_tribe"), + (8, "N3", 1u64, "air_nomad"), + (9, "N4", 1u64, "air_nomad"), + (5, "N5", 1u64, "air_nomad"), + (6, "N5", 2u64, "air_nomad"), + (5, "N6", 1u64, "fire_nation"), + (6, "N6", 1u64, "fire_nation"), + (3, "N7", 1u64, "air_nomad"), + (5, "N7", 1u64, "air_nomad"), + (3, "N8", 1u64, "fire_nation"), + (4, "N8", 2u64, "fire_nation"), + ]; + + for (ts, name, value, kind) in node_data { + graph + .add_node(ts, name, [("p1", Prop::U64(value))], Some(kind), None) + .unwrap(); + } + + graph + } + + #[test] + fn test_nodes_filters() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + CachedGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_graph, + CachedGraphTransformer, + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_filters_w() { + // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_filters_pg_w() { + let filter = NodeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2", "N5", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + } + + mod test_edges_filter_cached_view_graph { + use raphtory::{ + db::api::view::StaticGraphViewOps, + prelude::{AdditionOps, EdgeFilter}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_test_utils::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestVariants, + }; + + use crate::test_filters_cached_view::{ + CachedGraphTransformer, WindowedCachedGraphTransformer, + }; + use raphtory::db::graph::views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }; + + fn init_graph(graph: G) -> G { + let edge_data = vec![ + (6, "N1", "N2", 2u64), + (7, "N1", "N2", 1u64), + (6, "N2", "N3", 1u64), + (7, "N2", "N3", 2u64), + (8, "N3", "N4", 1u64), + (9, "N4", "N5", 1u64), + (5, "N5", "N6", 1u64), + (6, "N5", "N6", 2u64), + (5, "N6", "N7", 1u64), + (6, "N6", "N7", 1u64), + (3, "N7", "N8", 1u64), + (5, "N7", "N8", 1u64), + (3, "N8", "N1", 1u64), + (4, "N8", "N1", 2u64), + ]; + + for (ts, src, dst, p1_val) in edge_data { + graph + .add_edge(ts, src, dst, [("p1", Prop::U64(p1_val))], None) + .unwrap(); + } + + graph + } + + #[test] + fn test_edges_filters() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + CachedGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + CachedGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_filter_w() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_w() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; + assert_filter_edges_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowedCachedGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + } +} diff --git a/raphtory-test-utils/tests/db_tests.rs b/raphtory-test-utils/tests/db_tests.rs new file mode 100644 index 0000000000..cd61ef8d78 --- /dev/null +++ b/raphtory-test-utils/tests/db_tests.rs @@ -0,0 +1,3895 @@ +use bigdecimal::BigDecimal; +use chrono::NaiveDateTime; +use itertools::Itertools; +use proptest::{arbitrary::any, prop_assert, prop_assert_eq, proptest, sample::subsequence}; +#[cfg(feature = "proto")] +use raphtory::serialise::StableDecode; +use raphtory::{ + algorithms::{ + centrality::{degree_centrality::degree_centrality, pagerank::unweighted_page_rank}, + components::weakly_connected_components, + }, + db::{ + api::{ + properties::internal::InternalMetadataOps, + state::MergePriority, + view::{ + internal::{GraphTimeSemanticsOps, InternalEdgeFilterOps}, + EdgeViewOps, LayerOps, NodeViewOps, TimeOps, + }, + }, + graph::{ + edge::EdgeView, edges::Edges, graph::assert_graph_equal, path::PathFromNode, + views::deletion_graph::PersistentGraph, + }, + }, + errors::GraphError, + graphgen::random_attachment::random_attachment, + prelude::*, +}; +use raphtory_api::core::{ + entities::{LayerId, GID, VID}, + storage::{ + arc_str::{ArcStr, OptionAsStr}, + timeindex::{AsTime, EventTime}, + }, + utils::{ + logging::global_info_logger, + time::{ParseTimeError, TryIntoTime, TryIntoTimeNeedsEventId}, + }, +}; +use raphtory_storage::{core_ops::CoreGraphOps, mutation::addition_ops::InternalAdditionOps}; +use raphtory_test_utils::{ + test_storage, + test_utils::{ + build_graph, build_graph_strat, EdgeFixture, EdgeUpdatesFixture, GraphFixture, NodeFixture, + PropUpdatesFixture, + }, +}; +use rayon::{join, prelude::*}; +use std::{ + collections::{HashMap, HashSet}, + ops::{Deref, Range}, + sync::Arc, +}; +#[cfg(feature = "proto")] +use tempfile::TempDir; +use tracing::{error, info}; + +#[test] +fn edge_metadata() -> Result<(), GraphError> { + let g = Graph::new(); + + g.add_edge(0, 0, 0, NO_PROPS, None)?; + g.add_edge(0, 0, 1, NO_PROPS, None)?; + + g.edge(0, 0).unwrap().update_metadata( + vec![("x".to_string(), Prop::map([("n", Prop::U64(23))]))], + None, + )?; + g.edge(0, 1).unwrap().update_metadata( + vec![( + "a".to_string(), + Prop::map([("a", Prop::U8(1)), ("b", Prop::str("baa"))]), + )], + None, + )?; + + let e1 = g.edge(0, 0).unwrap(); + let actual = e1.metadata().as_map(); + assert_eq!(actual.get("x"), Some(&Prop::map([("n", Prop::U64(23))]))); + + let e2 = g.edge(0, 1).unwrap(); + let actual = e2.metadata().as_vec(); + assert_eq!( + actual, + vec![( + "a".into(), + Prop::map([("b", Prop::str("baa")), ("a", Prop::U8(1))]) + )] + ); + Ok(()) +} + +#[test] +fn test_empty_graph() { + let graph = Graph::new(); + test_storage!(&graph, |graph| { + assert!(!graph.has_edge(1, 2)); + + let test_time = 42; + let result = graph.at(test_time); + assert!(result.start.is_some()); + assert!(result.end.is_some()); + + let result = graph.after(test_time); + assert!(result.start.is_some()); + assert!(result.end.is_none()); + + let result = graph.before(test_time); + assert!(result.start.is_none()); + assert!(result.end.is_some()); + + assert_eq!( + graph.metadata_keys().collect::>(), + Vec::::new() + ); + assert_eq!( + graph.metadata_ids().collect::>(), + Vec::::new() + ); + assert_eq!( + graph.metadata_values().collect::>(), + Vec::>::new() + ); + assert!(graph.metadata().get_by_id(1).is_none()); + assert!(graph.get_metadata_id("1").is_none()); + assert!(graph.get_metadata(1).is_none()); + assert_eq!(graph.count_nodes(), 0); + assert_eq!(graph.count_edges(), 0); + assert_eq!(graph.count_temporal_edges(), 0); + + assert!(graph.start().is_none()); + assert!(graph.end().is_none()); + assert_eq!(graph.earliest_time(), None); + // assert!(graph.timeline_end().is_none()); + + assert!(graph.is_empty()); + + assert!(graph.nodes().collect().is_empty()); + assert_eq!(graph.edges().collect(), Vec::>::new()); + assert!(!graph.internal_edge_filtered()); + assert!(graph.edge(1, 2).is_none()); + assert!(graph.latest_time_global().is_none()); + assert!(graph + .latest_time_window(EventTime::start(1), EventTime::start(2)) + .is_none()); + assert!(graph.latest_time().is_none()); + assert!(graph.latest_time_global().is_none()); + assert!(graph.earliest_time_global().is_none()); + }); +} + +#[test] +fn test_multithreaded_add_edge() { + proptest!(|(edges: Vec<(u64, u64)>)| { + let g = Graph::new(); + edges.par_iter().enumerate().for_each(|(t, (i, j))| { + g.add_edge(t as i64, *i, *j, NO_PROPS, None).unwrap(); + }); + prop_assert!(edges.iter().all(|(i, j)| g.has_edge(*i, *j)) && g.count_temporal_edges() == edges.len()); + }); +} + +#[test] +fn test_multithreaded_add_edge_both_directions() { + proptest!(|(edges: Vec<(u64, u64)>)| { + let g = Graph::new(); + let mut self_loop_count = 0; + for (src, dst) in edges.iter() { + if src == dst { + self_loop_count += 1; + } + // try to maximise the chance that both directions of the edge are added in parallel + join(|| { + g.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); + }, || {g.add_edge(0, *dst, *src, NO_PROPS, None).unwrap();}); + } + + prop_assert!(edges.iter().all(|(i, j)| g.has_edge(*i, *j) && g.has_edge(*j, *i)) && g.count_temporal_edges() == 2*edges.len()-self_loop_count); + }); +} + +#[test] +fn add_node_grows_graph_len() { + proptest!(|(vs: Vec<(i64, u64)>)| { + let g = Graph::new(); + + let expected_len = vs.iter().map(|(_, v)| v).sorted().dedup().count(); + for (t, v) in vs { + g.add_node(t, v, NO_PROPS, None, None) + .map_err(|err| error!("{:?}", err)) + .ok(); + } + + prop_assert_eq!(g.count_nodes(), expected_len); + }); +} + +#[test] +fn add_node_gets_names() { + proptest!(|(vs: Vec)| { + global_info_logger(); + let g = Graph::new(); + + let expected_len = vs.iter().sorted().dedup().count(); + for (t, name) in vs.iter().enumerate() { + g.add_node(t as i64, name.clone(), NO_PROPS, None, None) + .map_err(|err| info!("{:?}", err)) + .ok(); + } + + prop_assert_eq!(g.count_nodes(), expected_len); + + let res = vs.iter().all(|name| { + let v = g.node(name.clone()).unwrap(); + v.name() == name.clone() + }); + prop_assert!(res); + }); +} + +#[test] +fn add_edge_grows_graph_edge_len() { + proptest!(|(edges: Vec<(i64, u64, u64)>)| { + let g = Graph::new(); + + let unique_nodes_count = edges + .iter() + .flat_map(|(_, src, dst)| vec![src, dst]) + .sorted() + .dedup() + .count(); + + let unique_edge_count = edges + .iter() + .map(|(_, src, dst)| (src, dst)) + .unique() + .count(); + + for (t, src, dst) in edges { + g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + + prop_assert_eq!(g.count_nodes(), unique_nodes_count); + prop_assert_eq!(g.count_edges(), unique_edge_count); + }); +} + +#[test] +fn simple_add_edge() { + let edges = vec![(1, 1, 2), (2, 2, 3), (3, 3, 4)]; + + let g = Graph::new(); + for &(t, src, dst) in edges.iter() { + g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + + assert!(edges.iter().all(|&(_, src, dst)| g.has_edge(src, dst))) +} + +#[test] +fn add_edge_works() { + proptest!(|(edges: Vec<(i64, u64, u64)>)| { + let g = Graph::new(); + for &(t, src, dst) in edges.iter() { + g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + + prop_assert!(edges.iter().all(|&(_, src, dst)| g.has_edge(src, dst))); + }); +} + +#[test] +fn get_edge_works() { + proptest!(|(edges: Vec<(i64, u64, u64)>)| { + let g = Graph::new(); + for &(t, src, dst) in edges.iter() { + g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + + prop_assert!(edges + .iter() + .all(|&(_, src, dst)| g.edge(src, dst).is_some())); + }); +} + +#[test] +fn import_from_another_graph() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + + assert_eq!(g_b.history(), vec![1]); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + let gg = Graph::new(); + let res = gg.import_node(&g_a, false).unwrap(); + assert_eq!(res.name(), "A"); + assert_eq!(res.history(), vec![0]); + let res = gg.import_node(&g_b, false).unwrap(); + assert_eq!(res.name(), "B"); + assert_eq!(res.history(), vec![1]); + assert_eq!(res.properties().get("temp").unwrap(), Prop::Bool(true)); + assert_eq!(res.metadata().get("con").unwrap(), Prop::I64(11)); + + let gg = Graph::new(); + gg.add_node(1, "B", NO_PROPS, None, None).unwrap(); + let res = gg.import_nodes(vec![&g_a, &g_b], false); + match res { + Err(GraphError::NodesExistError(ids)) => { + assert_eq!( + ids.into_iter() + .map(|id| id.to_string()) + .collect::>(), + vec!["B"], + ); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), + } + + assert_eq!(gg.node("A"), None); + + let gg = Graph::new(); + gg.import_nodes(vec![&g_a, &g_b], false).unwrap(); + assert_eq!(gg.nodes().name().collect_vec(), vec!["A", "B"]); + + let e_a_b = g.add_edge(2, "A", "B", NO_PROPS, None).unwrap(); + let res = gg.import_edge(&e_a_b, false).unwrap(); + assert_eq!( + (res.src().name(), res.dst().name()), + (e_a_b.src().name(), e_a_b.dst().name()) + ); + let e_a_b_p = g + .add_edge( + 3, + "A", + "B", + vec![("etemp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); + let gg = Graph::new(); + let _ = gg.add_node(0, "B", NO_PROPS, None, None); + let res = gg.import_edge(&e_a_b_p, false).expect("Failed to add edge"); + assert_eq!(res.properties().as_vec(), e_a_b_p.properties().as_vec()); + + let e_c_d = g.add_edge(4, "C", "D", NO_PROPS, None).unwrap(); + + let gg = Graph::new(); + gg.import_edges(vec![&e_a_b, &e_c_d], false).unwrap(); + assert_eq!(gg.edges().len(), 2); + + let gg = Graph::new(); + gg.add_edge(1, "C", "D", NO_PROPS, None).unwrap(); + let res = gg.import_edges(vec![&e_a_b, &e_c_d], false); + match res { + Err(GraphError::EdgesExistError(duplicates)) => { + assert_eq!( + duplicates + .into_iter() + .map(|(x, y)| (x.to_string(), y.to_string())) + .collect::>(), + vec![("C".to_string(), "D".to_string())] + ); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), + } + assert_eq!(gg.edge("A", "B"), None); +} + +#[test] +fn import_node_as() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + + let gg = Graph::new(); + let res = gg.import_node_as(&g_a, "X", false).unwrap(); + assert_eq!(res.name(), "X"); + assert_eq!(res.history(), vec![0]); + + let _ = gg.add_node(1, "Y", NO_PROPS, None, None).unwrap(); + let res = gg.import_node_as(&g_b, "Y", false); + match res { + Err(GraphError::NodeExistsError(id)) => { + assert_eq!(id.to_string(), "Y"); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), + } + + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["X", "Y"]); // Nodes up until first failure are imported + let y = gg.node("Y").unwrap(); + + assert_eq!(y.name(), "Y"); + assert_eq!(y.history().t(), vec![1]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); +} + +#[test] +fn import_node_as_merge() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + + let gg = Graph::new(); + gg.add_node(1, "Y", NO_PROPS, None, None).unwrap(); + + let res = gg.import_node_as(&g_a, "X", false).unwrap(); + assert_eq!(res.name(), "X"); + assert_eq!(res.history(), vec![0]); + + let res = gg.import_node_as(&g_b, "Y", true).unwrap(); + assert_eq!(res.name(), "Y"); + assert_eq!(res.history(), vec![1, 1]); + assert_eq!(res.properties().get("temp").unwrap(), Prop::Bool(true)); + assert_eq!(res.metadata().get("con").unwrap(), Prop::I64(11)); +} + +#[test] +fn import_nodes_as() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + let g_c = g.add_node(0, "C", NO_PROPS, None, None).unwrap(); + + let gg = Graph::new(); + gg.add_node(1, "Q", NO_PROPS, None, None).unwrap(); + gg.add_node(1, "R", NO_PROPS, None, None).unwrap(); + let res = gg.import_nodes_as(vec![&g_a, &g_b, &g_c], vec!["P", "Q", "R"], false); + match res { + Err(GraphError::NodesExistError(ids)) => { + assert_eq!( + ids.into_iter() + .map(|id| id.to_string()) + .collect::>(), + vec!["Q", "R"], + ); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), + } + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["Q", "R"]); // Nodes up until first failure are imported + let y = gg.node("Q").unwrap(); + assert_eq!(y.name(), "Q"); + assert_eq!(y.history(), vec![1]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); +} + +#[test] +fn import_nodes_as_merge() { + let g = Graph::new(); + let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + + let gg = Graph::new(); + gg.add_node(1, "Q", NO_PROPS, None, None).unwrap(); + gg.import_nodes_as(vec![&g_a, &g_b], vec!["P", "Q"], true) + .unwrap(); + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["P", "Q"]); + let y = gg.node("Q").unwrap(); + assert_eq!(y.name(), "Q"); + assert_eq!(y.history().t().collect(), vec![1, 1]); + assert_eq!(y.properties().get("temp").unwrap(), Prop::Bool(true)); + assert_eq!(y.metadata().get("con").unwrap(), Prop::I64(11)); +} + +#[test] +fn import_edge_as() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]) + .unwrap(); + let e_a_b = g + .add_edge( + 2, + "A", + "B", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); + let e_b_c = g + .add_edge( + 2, + "B", + "C", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); + + let gg = Graph::new(); + gg.add_edge(1, "X", "Y", NO_PROPS, None).unwrap(); + gg.import_edge_as(&e_b_c, ("Y", "Z"), false).unwrap(); + let res = gg.import_edge_as(&e_a_b, ("X", "Y"), false); + match res { + Err(GraphError::EdgeExistsError(src_id, dst_id)) => { + assert_eq!(src_id.to_string(), "X"); + assert_eq!(dst_id.to_string(), "Y"); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), + } + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["X", "Y", "Z"]); + let x = gg.node("X").unwrap(); + assert_eq!(x.name(), "X"); + assert_eq!(x.history(), vec![1]); + let y = gg.node("Y").unwrap(); + assert_eq!(y.name(), "Y"); + assert_eq!(y.history(), vec![1, 2]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); + + let e_src = gg.edge("X", "Y").unwrap().src().name(); + let e_dst = gg.edge("X", "Y").unwrap().dst().name(); + assert_eq!(e_src, "X"); + assert_eq!(e_dst, "Y"); + + let props = gg.edge("X", "Y").unwrap().properties().as_vec(); + assert_eq!(props, vec![]); +} + +#[test] +fn import_edge_as_merge() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + let e_a_b = g + .add_edge( + 2, + "A", + "B", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); + + let gg = Graph::new(); + let _ = gg.add_edge(3, "X", "Y", NO_PROPS, None).unwrap(); + let res = gg.import_edge_as(&e_a_b, ("X", "Y"), true).unwrap(); + assert_eq!(res.src().name(), "X"); + assert_eq!(res.dst().name(), "Y"); + assert_eq!(res.properties().as_vec(), e_a_b.properties().as_vec()); + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["X", "Y"]); + let x = gg.node("X").unwrap(); + assert_eq!(x.name(), "X"); + assert_eq!(x.history(), vec![2, 3]); + let y = gg.node("Y").unwrap(); + assert_eq!(y.name(), "Y"); + assert_eq!(y.history(), vec![2, 3]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); +} + +#[test] +fn import_edges_as() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]) + .unwrap(); + g.add_node(0, "C", NO_PROPS, None, None).unwrap(); + let e_a_b = g + .add_edge( + 2, + "A", + "B", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); + let e_b_c = g.add_edge(2, "B", "C", NO_PROPS, None).unwrap(); + + let gg = Graph::new(); + gg.add_edge(1, "Y", "Z", NO_PROPS, None).unwrap(); + let res = gg.import_edges_as([&e_a_b, &e_b_c], [("X", "Y"), ("Y", "Z")], false); + match res { + Err(GraphError::EdgesExistError(duplicates)) => { + assert_eq!( + duplicates + .into_iter() + .map(|(x, y)| (x.to_string(), y.to_string())) + .collect::>(), + vec![("Y".to_string(), "Z".to_string())] + ); + } + Err(e) => panic!("Unexpected error: {:?}", e), + Ok(_) => panic!("Expected error but got Ok"), + } + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["Y", "Z"]); + let y = gg.node("Y").unwrap(); + assert_eq!(y.name(), "Y"); + assert_eq!(y.history(), vec![1]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); + let x = gg.node("Z").unwrap(); + assert_eq!(x.name(), "Z"); + assert_eq!(x.history(), vec![1]); + + assert!(gg.edge("X", "Y").is_none()); + + let e_y_z = gg.edge("Y", "Z").unwrap(); + assert_eq!( + (e_y_z.src().name().as_str(), e_y_z.dst().name().as_str()), + ("Y", "Z") + ); + + let props = e_y_z.properties().as_vec(); + assert_eq!(props, vec![]); +} + +#[test] +fn import_edges_as_merge() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, None, None).unwrap(); + let g_b = g + .add_node( + 1, + "B", + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); + let e_a_b = g + .add_edge( + 2, + "A", + "B", + vec![("e_temp".to_string(), Prop::Bool(false))], + None, + ) + .unwrap(); + + let gg = Graph::new(); + gg.add_edge(3, "X", "Y", NO_PROPS, None).unwrap(); + gg.import_edges_as([&e_a_b], [("X", "Y")], true).unwrap(); + + let e_x_y = gg.edge("X", "Y").unwrap(); + assert_eq!( + (e_x_y.src().name().as_str(), e_x_y.dst().name().as_str()), + ("X", "Y") + ); + assert_eq!(e_x_y.properties().get("e_temp").unwrap(), Prop::Bool(false)); + + let mut nodes = gg.nodes().name().collect_vec(); + nodes.sort(); + assert_eq!(nodes, vec!["X", "Y"]); + let x = gg.node("X").unwrap(); + assert_eq!(x.name(), "X"); + assert_eq!(x.history(), vec![2, 3]); + let y = gg.node("Y").unwrap(); + assert_eq!(y.name(), "Y"); + assert_eq!(y.history(), vec![2, 3]); + assert_eq!(y.properties().get("temp"), None); + assert_eq!(y.metadata().get("con"), None); +} + +#[test] +fn props_with_layers() { + global_info_logger(); + let g = Graph::new(); + g.add_edge(0, "A", "B", NO_PROPS, None).unwrap(); + let ed = g.edge("A", "B").unwrap(); + ed.add_metadata(vec![("CCC", Prop::str("RED"))], None) + .unwrap(); + info!("{:?}", ed.metadata().as_map()); + g.add_edge(0, "A", "B", NO_PROPS, Some("LAYERONE")).unwrap(); + ed.add_metadata(vec![("CCC", Prop::str("BLUE"))], Some("LAYERONE")) + .unwrap(); + info!("{:?}", ed.metadata().as_map()); +} + +#[test] +#[cfg(feature = "proto")] +fn graph_save_to_load_from_file() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let g = Graph::new(); + + for (t, src, dst) in &vs { + g.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + + let tmp_raphtory_path: TempDir = TempDir::new().unwrap(); + + let graph_path = format!("{}/graph.bin", tmp_raphtory_path.path().display()); + g.encode(&graph_path).unwrap(); + + // Load from files + let g2 = Graph::decode(&graph_path).unwrap(); + + assert_eq!(g, g2); +} + +#[test] +fn has_edge() { + let g = Graph::new(); + g.add_edge(1, 7, 8, NO_PROPS, None).unwrap(); + + assert!(!g.has_edge(8, 7)); + assert!(g.has_edge(7, 8)); + + g.add_edge(1, 7, 9, NO_PROPS, None).unwrap(); + + assert!(!g.has_edge(9, 7)); + assert!(g.has_edge(7, 9)); +} + +#[test] +fn has_edge_str() { + let g = Graph::new(); + g.add_edge(2, "haaroon", "northLondon", NO_PROPS, None) + .unwrap(); + assert!(g.has_edge("haaroon", "northLondon")); +} + +#[test] +fn graph_edge() { + let graph = Graph::new(); + let es = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + for (t, src, dst) in es { + graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let e = graph + .window(i64::MIN, i64::MAX) + .layers(Layer::Default) + .unwrap() + .edge(1, 3) + .unwrap(); + + assert_eq!(e.src().id().into_u64(), Some(1u64)); + assert_eq!(e.dst().id().into_u64(), Some(3u64)); + }); +} + +#[test] +fn graph_degree_window() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + + let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; + let actual = (1..=3) + .map(|i| { + let v = graph.node(i).unwrap(); + ( + v.window(-1, 7).in_degree(), + v.window(1, 7).out_degree(), + v.window(0, 1).degree(), + ) + }) + .collect::>(); + + assert_eq!(actual, expected); +} + +#[test] +fn graph_edges_window() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; + let actual = (1..=3) + .map(|i| { + let v = graph.node(i).unwrap(); + ( + v.window(-1, 7).in_edges().iter().count(), + v.window(1, 7).out_edges().iter().count(), + v.window(0, 1).edges().iter().count(), + ) + }) + .collect::>(); + + assert_eq!(actual, expected); +} + +#[test] +fn test_explode_layers_time() { + global_info_logger(); + let g = Graph::new(); + g.add_edge( + 1, + 1, + 2, + vec![("duration".to_string(), Prop::U32(5))], + Some("a"), + ) + .map_err(|err| error!("{:?}", err)) + .ok(); + g.add_edge( + 2, + 1, + 2, + vec![("duration".to_string(), Prop::U32(5))], + Some("a"), + ) + .map_err(|err| error!("{:?}", err)) + .ok(); + g.add_edge( + 3, + 1, + 2, + vec![("duration".to_string(), Prop::U32(5))], + Some("a"), + ) + .map_err(|err| error!("{:?}", err)) + .ok(); + g.add_edge( + 4, + 1, + 2, + vec![("duration".to_string(), Prop::U32(6))], + Some("b"), + ) + .map_err(|err| error!("{:?}", err)) + .ok(); + g.add_edge(5, 1, 2, NO_PROPS, Some("c")) + .map_err(|err| error!("{:?}", err)) + .ok(); + + assert_eq!(g.latest_time().unwrap().t(), 5); + + let earliest_times = g + .edge(1, 2) + .unwrap() + .explode_layers() + .earliest_time() + .map(|t| t.unwrap().t()) + .collect_vec(); + + assert_eq!(earliest_times, vec![1, 4, 5]); + + let latest_times = g + .edge(1, 2) + .unwrap() + .explode_layers() + .latest_time() + .map(|t| t.unwrap().t()) + .collect_vec(); + + assert_eq!(latest_times, vec![3, 4, 5]); +} + +#[test] +fn time_test() { + global_info_logger(); + let g = Graph::new(); + + assert_eq!(g.latest_time(), None); + assert_eq!(g.earliest_time(), None); + + g.add_node(5, 1, NO_PROPS, None, None) + .map_err(|err| error!("{:?}", err)) + .ok(); + + assert_eq!(g.latest_time().unwrap().t(), 5); + assert_eq!(g.earliest_time().unwrap().t(), 5); + + let g = Graph::new(); + + g.add_edge(10, 1, 2, NO_PROPS, None).unwrap(); + assert_eq!(g.latest_time().unwrap().t(), 10); + assert_eq!(g.earliest_time().unwrap().t(), 10); + + g.add_node(5, 1, NO_PROPS, None, None) + .map_err(|err| error!("{:?}", err)) + .ok(); + assert_eq!(g.latest_time().unwrap().t(), 10); + assert_eq!(g.earliest_time().unwrap().t(), 5); + + g.add_edge(20, 3, 4, NO_PROPS, None).unwrap(); + assert_eq!(g.latest_time().unwrap().t(), 20); + assert_eq!(g.earliest_time().unwrap().t(), 5); + + random_attachment(&g, 100, 10, None); + assert_eq!(g.latest_time().unwrap().t(), 126); + assert_eq!(g.earliest_time().unwrap().t(), 5); +} + +#[test] +fn test_metadata_props() { + let g = Graph::new(); + let n = g.add_node(1, 1, [("p1", 1)], None, None).unwrap(); + n.add_metadata([("m1", 1)]).unwrap(); + let n = g.add_node(1, 2, [("p2", 2)], None, None).unwrap(); + n.add_metadata([("m2", 2)]).unwrap(); + + let n1_meta = g + .node(1) + .unwrap() + .metadata() + .keys() + .map(|s| s.to_string()) + .collect::>(); + assert_eq!(n1_meta, vec!["m1", "m2"]); + let n1_props = g + .node(1) + .unwrap() + .properties() + .keys() + .map(|s| s.to_string()) + .collect::>(); + assert_eq!(n1_props, vec!["p1", "p2"]); +} + +#[test] +fn metadata() { + let g = Graph::new(); + g.add_edge(0, 11, 22, NO_PROPS, None).unwrap(); + g.add_edge( + 0, + 11, + 11, + vec![("temp".to_string(), Prop::Bool(true))], + None, + ) + .unwrap(); + g.add_edge(0, 22, 33, NO_PROPS, None).unwrap(); + g.add_edge(0, 33, 11, NO_PROPS, None).unwrap(); + g.add_node( + 0, + 11, + vec![("temp".to_string(), Prop::Bool(true))], + None, + None, + ) + .unwrap(); + g.add_edge(0, 44, 55, NO_PROPS, None).unwrap(); + let v11 = g.node(11).unwrap(); + let v22 = g.node(22).unwrap(); + let v33 = g.node(33).unwrap(); + let v44 = g.node(44).unwrap(); + let v55 = g.node(55).unwrap(); + let edge1111 = g.edge(&v11, &v11).unwrap(); + let edge2233 = g.edge(&v22, &v33).unwrap(); + let edge3311 = g.edge(&v33, &v11).unwrap(); + + v11.add_metadata(vec![("a", Prop::U64(11)), ("b", Prop::I64(11))]) + .unwrap(); + v11.add_metadata(vec![("c", Prop::U32(11))]).unwrap(); + + v44.add_metadata(vec![("e", Prop::U8(1))]).unwrap(); + v55.add_metadata(vec![("f", Prop::U16(1))]).unwrap(); + edge1111 + .add_metadata(vec![("d", Prop::U64(1111))], None) + .unwrap(); + edge3311 + .add_metadata(vec![("a", Prop::U64(3311))], None) + .unwrap(); + + // cannot add properties to non-existant layer + assert!(edge1111 + .add_metadata([("test", "test")], Some("test")) + .is_err()); + + // cannot change property type + assert!(v22.add_metadata(vec![("b", Prop::U64(22))]).is_err()); + + // TODO: Revisit this test after metadata handling is finalised. + // Refer to the `test_metadata_props` test for context. + // assert_eq!( + // v11.metadata().keys().collect::>(), + // vec!["a", "b", "c"] + // ); + // assert!(v22.metadata().keys().next().is_none()); + // assert!(v33.metadata().keys().next().is_none()); + // assert_eq!(v44.metadata().keys().collect::>(), vec!["e"]); + // assert_eq!(v55.metadata().keys().collect::>(), vec!["f"]); + + assert_eq!( + edge1111.metadata().keys().collect::>(), + vec!["d", "a"] // all edges get all ids anyhow + ); + assert_eq!( + edge3311.metadata().keys().collect::>(), + vec!["d", "a"] + ); + assert_eq!( + edge2233.metadata().keys().collect::>(), + vec!["d", "a"] + ); + + assert_eq!(v11.metadata().get("a"), Some(Prop::U64(11))); + assert_eq!(v11.metadata().get("b"), Some(Prop::I64(11))); + assert_eq!(v11.metadata().get("c"), Some(Prop::U32(11))); + assert_eq!(v22.metadata().get("b"), None); + assert_eq!(v44.metadata().get("e"), Some(Prop::U8(1))); + assert_eq!(v55.metadata().get("f"), Some(Prop::U16(1))); + assert_eq!(v22.metadata().get("a"), None); + assert_eq!(edge1111.metadata().get("d"), Some(Prop::U64(1111))); + assert_eq!(edge3311.metadata().get("a"), Some(Prop::U64(3311))); + assert_eq!(edge2233.metadata().get("a"), None); + + // cannot add properties to non-existant layer + assert!(edge1111 + .add_metadata([("test", "test")], Some("test")) + .is_err()); + g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); + // cannot add properties to layer without updates + assert!(edge1111 + .add_metadata([("test", "test")], Some("test")) + .is_err()); +} + +#[test] +fn temporal_node_rows_1_node() { + let graph = Graph::new(); + + graph + .add_node(0, 1, [("cool".to_string(), Prop::Bool(true))], None, None) + .unwrap(); + + test_storage!(&graph, |graph| { + let actual = graph + .node(1) + .unwrap() + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); + + assert_eq!(actual, vec![(0.into(), vec![Prop::Bool(true)])]); + }); + + graph + .add_node(0, 1, [("coolio".to_string(), Prop::U64(9))], None, None) + .unwrap(); + + test_storage!(&graph, |graph| { + let actual = graph + .node(1) + .unwrap() + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); + + assert_eq!( + actual, + vec![ + (EventTime::new(0, 0), vec![Prop::Bool(true)]), + (EventTime::new(0, 1), vec![Prop::U64(9)]) + ] + ); + }); + + graph + .add_node( + 1, + 1, + [ + ("cool".to_string(), Prop::Bool(false)), + ("coolio".to_string(), Prop::U64(19)), + ], + None, + None, + ) + .unwrap(); + + test_storage!(&graph, |graph| { + let actual = graph + .node(1) + .unwrap() + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); + + let expected = vec![ + (EventTime::new(0, 0), vec![Prop::Bool(true)]), + (EventTime::new(0, 1), vec![Prop::U64(9)]), + (EventTime::new(1, 2), vec![Prop::Bool(false), Prop::U64(19)]), + ]; + assert_eq!(actual, expected); + }); + + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + // edge additions should not show up in prop rows + graph.add_edge(3, 1, 1, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let actual = graph + .node(1) + .unwrap() + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); + + let expected = vec![ + (EventTime::new(0, 0), vec![Prop::Bool(true)]), + (EventTime::new(0, 1), vec![Prop::U64(9)]), + (EventTime::new(1, 2), vec![Prop::Bool(false), Prop::U64(19)]), + (EventTime::new(2, 3), vec![]), + ]; + + assert_eq!(actual, expected); + }); +} + +#[test] +fn temporal_node_rows_nodes() { + let graph = Graph::new(); + let mut nodes = Vec::new(); + nodes.push( + graph + .add_node(0, 1, [("cool".to_string(), Prop::U64(1))], None, None) + .unwrap() + .node, + ); + nodes.push( + graph + .add_node(1, 2, [("cool".to_string(), Prop::U64(2))], None, None) + .unwrap() + .node, + ); + nodes.push( + graph + .add_node(2, 3, [("cool".to_string(), Prop::U64(3))], None, None) + .unwrap() + .node, + ); + + let prop_ids: Arc<[usize]> = graph.node_meta().temporal_prop_mapper().ids().collect(); + for (id, n) in nodes.into_iter().enumerate() { + let actual = graph + .core_graph() + .nodes() + .node(n) + .temp_prop_rows(prop_ids.clone()) + .map(|(t, _, row)| (t, row.into_iter().map(|(_, p)| p).collect::>())) + .collect::>(); + + let expected = vec![( + EventTime::new(id as i64, id), + vec![Prop::U64((id as u64) + 1)], + )]; + assert_eq!(actual, expected); + } +} + +#[test] +fn temporal_node_rows_window() { + let graph = Graph::new(); + graph + .add_node(0, 1, [("cool".to_string(), Prop::U64(1))], None, None) + .unwrap(); + graph + .add_node(1, 1, [("cool".to_string(), Prop::U64(2))], None, None) + .unwrap(); + graph + .add_node(2, 1, [("cool".to_string(), Prop::U64(3))], None, None) + .unwrap(); + + test_storage!(&graph, |graph| { + let prop_ids: Arc<[usize]> = graph.node_meta().temporal_prop_mapper().ids().collect(); + let get_rows = |vid: VID, range: Range| { + graph + .core_graph() + .nodes() + .node(vid) + .temp_prop_rows_range(Some(range), prop_ids.clone()) + .map(|(t, _, row)| (t, row.into_iter().map(|(_, p)| p).collect::>())) + .collect::>() + }; + let actual = get_rows(VID(0), EventTime::new(2, 0)..EventTime::new(3, 0)); + + let expected = vec![(EventTime::new(2, 2), vec![Prop::U64(3)])]; + + assert_eq!(actual, expected); + + let actual = get_rows(VID(0), EventTime::new(0, 0)..EventTime::new(3, 0)); + let expected = vec![ + (EventTime::new(0, 0), vec![Prop::U64(1)]), + (EventTime::new(1, 1), vec![Prop::U64(2)]), + (EventTime::new(2, 2), vec![Prop::U64(3)]), + ]; + + assert_eq!(actual, expected); + }); +} + +#[test] +fn temporal_props_node() { + let graph = Graph::new(); + + graph + .add_node(0, 1, [("cool".to_string(), Prop::Bool(true))], None, None) + .unwrap(); + + let v = graph.node(1).unwrap(); + + let actual = v.properties().get("cool"); + assert_eq!(actual, Some(Prop::Bool(true))); + + // we flip cool from true to false after t 3 + graph + .add_node(3, 1, [("cool".to_string(), Prop::Bool(false))], None, None) + .unwrap(); + + test_storage!(&graph, |graph| { + let wg = graph.window(3, 15); + let v = wg.node(1).unwrap(); + + let actual = v.properties().get("cool"); + assert_eq!(actual, Some(Prop::Bool(false))); + + let hist: Vec<_> = v + .properties() + .temporal() + .get("cool") + .unwrap() + .iter() + .map(|(x, y)| (x.t(), y)) + .collect(); + assert_eq!(hist, vec![(3, Prop::Bool(false))]); + + let v = graph.node(1).unwrap(); + + let hist: Vec<_> = v + .properties() + .temporal() + .get("cool") + .unwrap() + .iter() + .map(|(x, y)| (x.t(), y)) + .collect(); + assert_eq!(hist, vec![(0, Prop::Bool(true)), (3, Prop::Bool(false))]); + }); +} + +#[test] +fn temporal_props_edge() { + let graph = Graph::new(); + + graph + .add_edge(1, 0, 1, vec![("distance".to_string(), Prop::U32(5))], None) + .expect("add edge"); + + test_storage!(&graph, |graph| { + let e = graph.edge(0, 1).unwrap(); + + let prop = e.properties().get("distance").unwrap(); + assert_eq!(prop, Prop::U32(5)); + }); +} + +#[test] +fn graph_neighbours_window() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let expected = vec![ + (vec![1, 2], vec![1, 2, 3], vec![1]), + (vec![1], vec![], vec![]), + (vec![1], vec![], vec![]), + ]; + let actual = (1..=3) + .map(|i| { + let v = graph.node(i).unwrap(); + ( + v.window(-1, 7) + .in_neighbours() + .id() + .filter_map(|id| id.as_u64()) + .collect::>(), + v.window(1, 7) + .out_neighbours() + .id() + .filter_map(|id| id.as_u64()) + .collect::>(), + v.window(0, 1) + .neighbours() + .id() + .filter_map(|id| id.as_u64()) + .collect::>(), + ) + }) + .collect::>(); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn test_time_range_on_empty_graph() { + let graph = Graph::new(); + + test_storage!(&graph, |graph| { + let rolling = graph.rolling(1, None).unwrap().collect_vec(); + assert!(rolling.is_empty()); + + let expanding = graph.expanding(1).unwrap().collect_vec(); + assert!(expanding.is_empty()); + }); +} + +#[test] +fn test_add_node_with_nums() { + let graph = Graph::new(); + + graph.add_node(1, 831, NO_PROPS, None, None).unwrap(); + test_storage!(&graph, |graph| { + assert!(graph.has_node(831)); + + assert_eq!(graph.count_nodes(), 1); + }); +} + +#[test] +fn test_add_node_with_strings() { + let graph = Graph::new(); + + graph.add_node(0, "haaroon", NO_PROPS, None, None).unwrap(); + graph.add_node(1, "hamza", NO_PROPS, None, None).unwrap(); + test_storage!(&graph, |graph| { + assert!(graph.has_node("haaroon")); + assert!(graph.has_node("hamza")); + + assert_eq!(graph.count_nodes(), 2); + }); +} + +#[test] +fn layers() -> Result<(), GraphError> { + let graph = Graph::new(); + graph.add_edge(0, 11, 22, NO_PROPS, None)?; + graph.add_edge(0, 11, 33, NO_PROPS, None)?; + graph.add_edge(0, 33, 11, NO_PROPS, None)?; + graph.add_edge(0, 11, 22, NO_PROPS, Some("layer1"))?; + graph.add_edge(0, 11, 33, NO_PROPS, Some("layer2"))?; + graph.add_edge(0, 11, 44, NO_PROPS, Some("layer2"))?; + + assert!(graph.has_edge(11, 22)); + assert!(graph.default_layer().has_edge(11, 22)); + assert!(!graph.default_layer().has_edge(11, 44)); + assert!(!graph.layers("layer2")?.has_edge(11, 22)); + assert!(graph.layers("layer2")?.has_edge(11, 44)); + + assert!(graph.edge(11, 22).is_some()); + assert!(graph.layers(Layer::Default)?.edge(11, 44).is_none()); + assert!(graph.layers("layer2")?.edge(11, 22).is_none()); + assert!(graph.layers("layer2")?.edge(11, 44).is_some()); + + assert!(graph.exclude_layers("layer2")?.edge(11, 44).is_none()); + assert!(graph.exclude_layers("layer2")?.edge(11, 33).is_some()); + assert!(graph.exclude_layers("layer2")?.edge(11, 22).is_some()); + + let dft_layer = graph.default_layer(); + let layer1 = graph.layers("layer1")?; + let layer2 = graph.layers("layer2")?; + assert!(graph.layers("missing layer").is_err()); + + assert_eq!(graph.count_nodes(), 4); + assert_eq!(graph.count_edges(), 4); + assert_eq!(dft_layer.count_edges(), 3); + assert_eq!(layer1.count_edges(), 1); + assert_eq!(layer2.count_edges(), 2); + + let node = graph.node(11).unwrap(); + let node_dft = dft_layer.node(11).unwrap(); + let node1 = layer1.node(11).unwrap(); + let node2 = layer2.node(11).unwrap(); + + assert_eq!(node.degree(), 3); + assert_eq!(node_dft.degree(), 2); + assert_eq!(node1.degree(), 1); + assert_eq!(node2.degree(), 2); + + assert_eq!(node.out_degree(), 3); + assert_eq!(node_dft.out_degree(), 2); + assert_eq!(node1.out_degree(), 1); + assert_eq!(node2.out_degree(), 2); + + assert_eq!(node.in_degree(), 1); + assert_eq!(node_dft.in_degree(), 1); + assert_eq!(node1.in_degree(), 0); + assert_eq!(node2.in_degree(), 0); + + fn to_tuples<'graph, G: GraphViewOps<'graph>>(edges: Edges<'graph, G>) -> Vec<(u64, u64)> { + edges + .id() + .filter_map(|(s, d)| s.to_u64().zip(d.to_u64())) + .sorted() + .collect_vec() + } + + assert_eq!( + to_tuples(node.edges()), + vec![(11, 22), (11, 33), (11, 44), (33, 11)] + ); + assert_eq!( + to_tuples(node_dft.edges()), + vec![(11, 22), (11, 33), (33, 11)] + ); + assert_eq!(to_tuples(node1.edges()), vec![(11, 22)]); + assert_eq!(to_tuples(node2.edges()), vec![(11, 33), (11, 44)]); + + assert_eq!(to_tuples(node.in_edges()), vec![(33, 11)]); + assert_eq!(to_tuples(node_dft.in_edges()), vec![(33, 11)]); + assert_eq!(to_tuples(node1.in_edges()), vec![]); + assert_eq!(to_tuples(node2.in_edges()), vec![]); + + assert_eq!( + to_tuples(node.out_edges()), + vec![(11, 22), (11, 33), (11, 44)] + ); + assert_eq!(to_tuples(node_dft.out_edges()), vec![(11, 22), (11, 33)]); + assert_eq!(to_tuples(node1.out_edges()), vec![(11, 22)]); + assert_eq!(to_tuples(node2.out_edges()), vec![(11, 33), (11, 44)]); + + fn to_ids<'graph, G: GraphViewOps<'graph>>(neighbours: PathFromNode<'graph, G>) -> Vec { + neighbours + .iter() + .filter_map(|n| n.id().as_u64()) + .sorted() + .collect_vec() + } + + assert_eq!(to_ids(node.neighbours()), vec![22, 33, 44]); + assert_eq!(to_ids(node_dft.neighbours()), vec![22, 33]); + assert_eq!(to_ids(node1.neighbours()), vec![22]); + assert_eq!(to_ids(node2.neighbours()), vec![33, 44]); + + assert_eq!(to_ids(node.out_neighbours()), vec![22, 33, 44]); + assert_eq!(to_ids(node_dft.out_neighbours()), vec![22, 33]); + assert_eq!(to_ids(node1.out_neighbours()), vec![22]); + assert_eq!(to_ids(node2.out_neighbours()), vec![33, 44]); + + assert_eq!(to_ids(node.in_neighbours()), vec![33]); + assert_eq!(to_ids(node_dft.in_neighbours()), vec![33]); + assert!(to_ids(node1.in_neighbours()).is_empty()); + assert!(to_ids(node2.in_neighbours()).is_empty()); + Ok(()) +} + +#[test] +fn test_props() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("weight", Prop::I64(1))], None) + .unwrap(); + g.add_edge(1, 1, 2, [("weight", Prop::I64(2))], None) + .unwrap(); + g.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); + + let exploded = g.edge(1, 2).unwrap().explode(); + let res = exploded.properties().map(|p| p.as_vec()).collect_vec(); + assert_eq!( + res, + vec![ + vec![("weight".into(), Prop::I64(1))], + vec![("weight".into(), Prop::I64(2))], + vec![] + ] + ); +} + +#[test] +fn test_exploded_edge() { + let graph = Graph::new(); + graph + .add_edge(0, 1, 2, [("weight", Prop::I64(1))], None) + .unwrap(); + graph + .add_edge(1, 1, 2, [("weight", Prop::I64(2))], None) + .unwrap(); + graph + .add_edge(2, 1, 2, [("weight", Prop::I64(3))], None) + .unwrap(); + test_storage!(&graph, |graph| { + let exploded = graph.edge(1, 2).unwrap().explode(); + + let res = exploded.properties().map(|p| p.as_vec()).collect_vec(); + + let mut expected = Vec::new(); + for i in 1..4 { + expected.push(vec![("weight".into(), Prop::I64(i))]); + } + + assert_eq!(res, expected); + + let e = graph + .node(1) + .unwrap() + .edges() + .explode() + .properties() + .map(|p| p.as_vec()) + .collect_vec(); + assert_eq!(e, expected); + }); +} + +#[test] +fn test_edge_earliest_latest() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let mut res = graph.edge(1, 2).unwrap().earliest_time().unwrap(); + assert_eq!(res, 0); + + res = graph.edge(1, 2).unwrap().latest_time().unwrap(); + assert_eq!(res, 2); + + res = graph.at(1).edge(1, 2).unwrap().earliest_time().unwrap(); + assert_eq!(res, 1); + + res = graph.before(1).edge(1, 2).unwrap().earliest_time().unwrap(); + assert_eq!(res, 0); + + res = graph.after(1).edge(1, 2).unwrap().earliest_time().unwrap(); + assert_eq!(res, 2); + + res = graph.at(1).edge(1, 2).unwrap().latest_time().unwrap(); + assert_eq!(res, 1); + + res = graph.before(1).edge(1, 2).unwrap().latest_time().unwrap(); + assert_eq!(res, 0); + + res = graph.after(1).edge(1, 2).unwrap().latest_time().unwrap(); + assert_eq!(res, 2); + + let res_list: Vec = graph + .node(1) + .unwrap() + .edges() + .earliest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![0, 0]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .edges() + .latest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![2, 2]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .at(1) + .edges() + .earliest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![1, 1]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .before(1) + .edges() + .earliest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![0, 0]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .after(1) + .edges() + .earliest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![2, 2]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .at(1) + .edges() + .latest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![1, 1]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .before(1) + .edges() + .latest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![0, 0]); + + let res_list: Vec = graph + .node(1) + .unwrap() + .after(1) + .edges() + .latest_time() + .flatten() + .collect(); + assert_eq!(res_list, vec![2, 2]); + }); +} + +#[test] +fn node_properties() -> Result<(), GraphError> { + let g = Graph::new(); + + g.add_node( + 0, + 1, + [("t", Prop::str("wallet")), ("cost", Prop::F64(99.5))], + Some("a"), + None, + )?; + + let n1 = g.node(1).unwrap(); + g.add_node(1, 2, [("t", Prop::str("person"))], None, None)?; + g.add_node( + 6, + 3, + [ + ("list_prop", vec![1.1, 2.2, 3.3].into_prop_list()), + ("cost_b", Prop::F64(76.0)), + ], + Some("b"), + None, + )?; + + g.add_node( + 7, + 4, + [ + ("str_prop", Prop::str("hello")), + ("bool_prop", Prop::Bool(true)), + ], + Some("b"), + None, + )?; + + n1.add_metadata([("lol", Prop::str("smile"))])?; + + let node_1_props = n1 + .properties() + .iter() + .filter_map(|(k, v)| v.map(move |v| (k.to_string(), v))) + .collect::>(); + assert_eq!( + node_1_props, + vec![ + ("t".to_string(), Prop::str("wallet")), + ("cost".to_string(), Prop::F64(99.5)), + ] + ); + + assert_eq!(n1.metadata().as_vec(), [("lol".into(), "smile".into())]); + + Ok(()) +} + +#[test] +fn test_decimal_properties() { + let graph = Graph::new(); + let dec_prop = Prop::Decimal(BigDecimal::new(123456234234123123i64.into(), 9)); + graph + .add_node(0, 1, [("cost".to_string(), dec_prop.clone())], None, None) + .unwrap(); + + test_storage!(&graph, |graph| { + let node = graph.node(1).unwrap(); + let prop = node.properties().get("cost").unwrap(); + assert_eq!(prop, dec_prop); + }); +} + +#[test] +fn node_history_rows() { + let graph = Graph::new(); + graph + .add_node(1, 2, [("cool".to_string(), Prop::U64(1))], None, None) + .unwrap(); + graph + .add_node(0, 1, [("cool".to_string(), 1u64)], None, None) + .unwrap(); + graph + .add_node( + 1, + 1, + [ + ("coolio".to_string(), Prop::Bool(true)), + ("bla".to_string(), Prop::I64(2)), + ], + None, + None, + ) + .unwrap(); + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + graph + .add_node(1, 1, [("cool".to_string(), 3u64)], None, None) + .unwrap(); + + let node = graph.node(1).unwrap(); + + let actual = node + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); + + let expected = vec![ + (EventTime::new(0, 1), vec![Prop::U64(1)]), + (EventTime::new(1, 2), vec![Prop::Bool(true), Prop::I64(2)]), + (EventTime::new(1, 4), vec![Prop::U64(3)]), + (EventTime::new(2, 3), vec![]), + ]; + + assert_eq!(actual, expected); + + let node = graph.node(2).unwrap(); + + let actual = node + .rows() + .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) + .collect::>(); + + let expected = vec![(EventTime::new(1, 0), vec![Prop::U64(1)])]; + + assert_eq!(actual, expected); +} + +#[test] +fn check_node_history_str() { + let graph = Graph::new(); + + graph + .add_node(4, "Lord Farquaad", NO_PROPS, None, None) + .unwrap(); + graph + .add_node(6, "Lord Farquaad", NO_PROPS, None, None) + .unwrap(); + graph + .add_node(7, "Lord Farquaad", NO_PROPS, None, None) + .unwrap(); + graph + .add_node(8, "Lord Farquaad", NO_PROPS, None, None) + .unwrap(); + + let times_of_farquaad = graph.node("Lord Farquaad").unwrap().history(); + + assert_eq!(times_of_farquaad, [4, 6, 7, 8]); + + let view = graph.window(1, 8); + + let windowed_times_of_farquaad = view.node("Lord Farquaad").unwrap().history(); + assert_eq!(windowed_times_of_farquaad, [4, 6, 7]); +} + +#[test] +fn check_node_history_num() { + let graph = Graph::new(); + + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(3, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(4, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(8, 1, NO_PROPS, None, None).unwrap(); + + let times_of_one = graph.node(1).unwrap().history(); + + assert_eq!(times_of_one, [1, 2, 3, 4, 8]); + + let view = graph.window(1, 8); + + let windowed_times_of_one = view.node(1).unwrap().history(); + assert_eq!(windowed_times_of_one, [1, 2, 3, 4]); +} + +#[test] +fn check_edge_history() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(4, 1, 4, NO_PROPS, None).unwrap(); + let times_of_onetwo = graph.edge(1, 2).unwrap().history(); + let times_of_four = graph.edge(1, 4).unwrap().window(1, 5).history(); + let view = graph.window(2, 5); + let windowed_times_of_four = view.edge(1, 4).unwrap().window(2, 4).history(); + + assert_eq!(times_of_onetwo, [1, 3]); + assert_eq!(times_of_four, [4]); + assert!(windowed_times_of_four.is_empty()); + assert_eq!(graph.node(1).unwrap().edge_history_count(), 4); +} + +#[test] +fn check_node_edge_history_count() { + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(3, 0, 1, NO_PROPS, None).unwrap(); + + let node = graph.node(0).unwrap(); + assert_eq!(node.edge_history_count(), 2); + assert_eq!(node.after(1).edge_history_count(), 1); + assert_eq!(node.after(3).edge_history_count(), 0); +} + +use raphtory_storage::graph::nodes::node_storage_ops::NodeStorageOps; + +#[test] +fn check_edge_history_on_multiple_shards() { + let graph = Graph::new(); + + graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(4, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(5, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(6, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(7, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(8, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(9, 1, 4, NO_PROPS, None).unwrap(); + graph.add_edge(10, 1, 4, NO_PROPS, None).unwrap(); + + let times_of_onetwo = graph.edge(1, 2).unwrap().history(); + let times_of_four = graph.edge(1, 4).unwrap().window(1, 5).history(); + let times_of_outside_window = graph.edge(1, 4).unwrap().window(1, 4).history(); + let times_of_four_higher = graph.edge(1, 4).unwrap().window(6, 11).history(); + + let view = graph.window(1, 11); + let windowed_times_of_four = view.edge(1, 4).unwrap().window(2, 5).history(); + let windowed_times_of_four_higher = view.edge(1, 4).unwrap().window(8, 11).history(); + + assert_eq!(times_of_onetwo, [1, 3]); + assert_eq!(times_of_four, [4]); + assert_eq!(times_of_four_higher, [6, 7, 8, 9, 10]); + assert!(times_of_outside_window.is_empty()); + assert_eq!(windowed_times_of_four, [4]); + assert_eq!(windowed_times_of_four_higher, [8, 9, 10]); +} + +#[derive(Debug)] +struct CustomTime<'a>(&'a str, &'a str); + +impl TryIntoTimeNeedsEventId for CustomTime<'_> {} + +impl<'a> TryIntoTime for CustomTime<'a> { + fn try_into_time(self) -> Result { + let CustomTime(time, fmt) = self; + let time = NaiveDateTime::parse_from_str(time, fmt)?; + let time = time.and_utc().timestamp_millis(); + Ok(EventTime::from(time)) + } +} + +#[test] +fn test_ingesting_timestamps() { + let earliest_time = "2022-06-06 12:34:00".try_into_time().unwrap().t(); + let latest_time = "2022-06-07 12:34:00".try_into_time().unwrap().t(); + + let g = Graph::new(); + g.add_node("2022-06-06T12:34:00.000", 0, NO_PROPS, None, None) + .unwrap(); + g.add_edge("2022-06-07T12:34:00", 1, 2, NO_PROPS, None) + .unwrap(); + assert_eq!(g.earliest_time().unwrap().t(), earliest_time); + assert_eq!(g.latest_time().unwrap().t(), latest_time); + + let g = Graph::new(); + let fmt = "%Y-%m-%d %H:%M"; + + g.add_node(CustomTime("2022-06-06 12:34", fmt), 0, NO_PROPS, None, None) + .unwrap(); + g.add_edge(CustomTime("2022-06-07 12:34", fmt), 1, 2, NO_PROPS, None) + .unwrap(); + assert_eq!(g.earliest_time().unwrap(), earliest_time); + assert_eq!(g.latest_time().unwrap(), latest_time); +} + +#[test] +fn test_prop_display_str() { + let mut prop = Prop::Str("hello".into()); + assert_eq!(format!("{}", prop), "hello"); + + prop = Prop::I32(42); + assert_eq!(format!("{}", prop), "42"); + + prop = Prop::I64(9223372036854775807); + assert_eq!(format!("{}", prop), "9223372036854775807"); + + prop = Prop::U32(4294967295); + assert_eq!(format!("{}", prop), "4294967295"); + + prop = Prop::U64(18446744073709551615); + assert_eq!(format!("{}", prop), "18446744073709551615"); + + prop = Prop::U8(255); + assert_eq!(format!("{}", prop), "255"); + + prop = Prop::U16(65535); + assert_eq!(format!("{}", prop), "65535"); + + prop = Prop::F32(3.15159); + assert_eq!(format!("{}", prop), "3.15159"); + + prop = Prop::F64(3.151592653589793); + assert_eq!(format!("{}", prop), "3.151592653589793"); + + prop = Prop::Bool(true); + assert_eq!(format!("{}", prop), "true"); +} + +#[test] +fn test_graph_metadata_proptest() { + proptest!(|(u64_props: HashMap)| { + let g = Graph::new(); + + let as_props = u64_props + .into_iter() + .map(|(name, value)| (name, Prop::U64(value))) + .collect::>(); + + g.add_metadata(as_props.clone()).unwrap(); + + let props_map = as_props.into_iter().collect::>(); + + prop_assert!(props_map + .into_iter() + .all(|(name, value)| g.metadata().get(&name).unwrap() == value)); + }); +} + +#[test] +fn test_graph_metadata() { + let g = Graph::new(); + + let as_props: Vec<(&str, Prop)> = + vec![("mylist", Prop::list(vec![Prop::I64(1), Prop::I64(2)]))]; + + g.add_metadata(as_props.clone()).unwrap(); + + let props_names = as_props + .into_iter() + .map(|(name, _)| name.into()) + .collect::>(); + + assert_eq!(g.metadata().keys().collect::>(), props_names); + + let data = vec![ + ("key1", Prop::I64(10)), + ("key2", Prop::I64(20)), + ("key3", Prop::I64(30)), + ]; + let as_props: Vec<(&str, Prop)> = vec![("mylist2", Prop::map(data))]; + + g.add_metadata(as_props.clone()).unwrap(); + + let props_names2: HashSet = as_props + .into_iter() + .map(|(name, _)| name.into()) + .collect::>(); + + assert_eq!( + g.metadata().keys().collect::>(), + props_names + .union(&props_names2) + .cloned() + .collect::>() + ); +} + +#[test] +fn test_add_graph_metadata_with_existing_key_throws_error() { + let g = Graph::new(); + g.add_metadata(vec![("style", Prop::str("red"))]).unwrap(); + + assert!(g.add_metadata(vec![("style", Prop::str("blue"))]).is_err()); + assert_eq!(g.metadata().get("style").unwrap(), Prop::str("red")); // Value is unchanged +} + +#[test] +fn test_graph_metadata_with_maps() { + let g = Graph::new(); + + let style_with_size = Prop::map(vec![("fill", Prop::str("red")), ("size", Prop::I64(5))]); + + let style_with_opacity = Prop::map(vec![ + ("fill", Prop::str("red")), + ("opacity", Prop::F64(0.4)), + ]); + + // Add first metadata and verify + g.add_metadata(vec![("style", style_with_size.clone())]) + .unwrap(); + let actual = g.metadata().get("style").unwrap(); + assert_eq!(actual, style_with_size.clone()); + + // Update metadata and verify + g.update_metadata(vec![("style", style_with_opacity.clone())]) + .unwrap(); + let actual = g.metadata().get("style").unwrap(); + assert_eq!(actual, style_with_opacity.clone()); + + // Add another metadata property and verify + let config = Prop::map(vec![ + ("theme", Prop::str("dark")), + ("version", Prop::I64(2)), + ]); + g.add_metadata(vec![("config", config.clone())]).unwrap(); + let actual_config = g.metadata().get("config").unwrap(); + assert_eq!(actual_config, config.clone()); + + // Verify style is still the updated value + let actual_style = g.metadata().get("style").unwrap(); + assert_eq!(actual_style, style_with_opacity.clone()); + + // Verify all metadata keys exist + let keys: Vec<_> = g.metadata().keys().sorted().collect(); + assert_eq!(keys, vec!["config", "style"]); +} + +#[test] +fn test_graph_metadata_names() { + proptest!(|(u64_props: HashMap)| { + let g = Graph::new(); + + let as_props = u64_props + .into_iter() + .map(|(name, value)| (name.into(), Prop::U64(value))) + .collect::>(); + + g.add_metadata(as_props.clone()).unwrap(); + + let props_names = as_props + .into_iter() + .map(|(name, _)| name) + .collect::>(); + + prop_assert_eq!(g.metadata().keys().collect::>(), props_names); + }); +} + +#[test] +fn test_graph_temporal_props() { + proptest!(|(str_props: HashMap)| { + global_info_logger(); + + let g = Graph::new(); + let (t0, t1) = (1, 2); + + // Split properties into two sets based on even/odd index + // Even-indexed properties go to t0, odd-indexed to t1 + let mut t0_props = HashMap::new(); + let mut t1_props = HashMap::new(); + + for (i, (name, value)) in str_props.iter().enumerate() { + let prop_name: ArcStr = name.as_str().into(); + let prop_value = Prop::from(value.as_str()); + + if i % 2 == 0 { + t0_props.insert(prop_name, prop_value); + } else { + t1_props.insert(prop_name, prop_value); + } + } + + g.add_properties(t0, t0_props.clone()).unwrap(); + g.add_properties(t1, t1_props.clone()).unwrap(); + + // Verify properties can be retrieved at their timestamps + for (name, expected_value) in t0_props.iter() { + let actual = g.properties().temporal().get(name).unwrap().at(t0); + + prop_assert_eq!( + actual, + Some(expected_value.clone()), + "Property '{}' at t0 has wrong value", + name + ); + } + + for (name, expected_value) in t1_props.iter() { + let actual_value = g.properties().temporal().get(name).unwrap().at(t1); + + prop_assert_eq!( + actual_value, + Some(expected_value.clone()), + "Property '{}' at t1 has wrong value", + name + ); + } + + // Verify iter_latest returns all t0 properties + let actual_t0_props: HashMap<_, _> = g + .at(t0) + .properties() + .temporal() + .iter_latest() + .map(|(prop_name, prop_value)| (prop_name.clone(), prop_value)) + .collect(); + + prop_assert_eq!( + actual_t0_props, + t0_props, + "iter_latest() at t0 returned wrong properties" + ); + + // Verify latest returns correct values for t1 properties + for (name, expected_value) in t1_props.iter() { + let actual = g + .at(t1) + .properties() + .temporal() + .get(name) + .and_then(|v| v.latest()); + + prop_assert_eq!( + actual, + Some(expected_value.clone()), + "Property '{}' latest() at t1 has wrong value", + name + ); + } + }); +} + +#[test] +fn test_graph_temporal_props_with_maps() { + let g = Graph::new(); + + let style_with_size = Prop::map(vec![("fill", Prop::str("red")), ("size", Prop::I64(5))]); + + let style_with_opacity = Prop::map(vec![ + ("fill", Prop::str("red")), + ("opacity", Prop::F64(0.4)), + ]); + + // Add temporal properties with nested maps at different timestamps + g.add_properties(0, vec![("style", style_with_size.clone())]) + .unwrap(); + g.add_properties(1, vec![("style", style_with_opacity.clone())]) + .unwrap(); + g.add_properties(2, vec![("style", style_with_size.clone())]) + .unwrap(); + g.add_properties(3, vec![("style", style_with_opacity.clone())]) + .unwrap(); + + // Verify properties can be retrieved at their timestamps + let actual_t0 = g.properties().temporal().get("style").unwrap().at(0); + assert_eq!(actual_t0, Some(style_with_size.clone())); + + let actual_t1 = g.properties().temporal().get("style").unwrap().at(1); + assert_eq!(actual_t1, Some(style_with_opacity.clone())); + + let actual_t2 = g.properties().temporal().get("style").unwrap().at(2); + assert_eq!(actual_t2, Some(style_with_size.clone())); + + let actual_t3 = g.properties().temporal().get("style").unwrap().at(3); + assert_eq!(actual_t3, Some(style_with_opacity.clone())); + + // Verify history returns all timestamps + let history: Vec<_> = g + .properties() + .temporal() + .get("style") + .unwrap() + .history() + .collect(); + + assert_eq!(history, vec![0, 1, 2, 3]); +} + +#[test] +fn test_temporal_edge_props_window() { + let graph = Graph::new(); + graph + .add_edge(1, 1, 2, vec![("weight".to_string(), Prop::I64(1))], None) + .unwrap(); + graph + .add_edge(2, 1, 2, vec![("weight".to_string(), Prop::I64(2))], None) + .unwrap(); + graph + .add_edge(3, 1, 2, vec![("weight".to_string(), Prop::I64(3))], None) + .unwrap(); + test_storage!(&graph, |graph| { + let e = graph + .node(1) + .unwrap() + .out_edges() + .into_iter() + .next() + .unwrap(); + let res: HashMap> = e + .window(1, 3) + .properties() + .temporal() + .iter() + .map(|(k, v)| (k.clone(), v.iter().map(|(x, y)| (x.t(), y)).collect())) + .collect(); + + let mut exp = HashMap::new(); + exp.insert( + ArcStr::from("weight"), + vec![(1, Prop::I64(1)), (2, Prop::I64(2))], + ); + assert_eq!(res, exp); + }); +} + +#[test] +fn test_node_early_late_times() { + let graph = Graph::new(); + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(3, 1, NO_PROPS, None, None).unwrap(); + + // FIXME: Node add without properties not showing up (Issue #46) + assert_eq!(graph.node(1).unwrap().earliest_time().unwrap().t(), 1); + assert_eq!(graph.node(1).unwrap().latest_time().unwrap().t(), 3); + + assert_eq!(graph.at(2).node(1).unwrap().earliest_time().unwrap().t(), 2); + assert_eq!(graph.at(2).node(1).unwrap().latest_time().unwrap().t(), 2); + + assert_eq!( + graph + .before(2) + .node(1) + .unwrap() + .earliest_time() + .unwrap() + .t(), + 1 + ); + assert_eq!( + graph.before(2).node(1).unwrap().latest_time().unwrap().t(), + 1 + ); + + assert_eq!( + graph.after(2).node(1).unwrap().earliest_time().unwrap().t(), + 3 + ); + assert_eq!( + graph.after(2).node(1).unwrap().latest_time().unwrap().t(), + 3 + ); +} + +#[test] +fn test_node_ids() { + let graph = Graph::new(); + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 3, NO_PROPS, None, None).unwrap(); + + assert_eq!(graph.nodes().id().sort_by_id(), vec![1u64, 2u64, 3u64]); + + let g_at = graph.at(1); + assert_eq!(g_at.nodes().id().sort_by_id(), vec![1u64, 2u64]); +} + +#[test] +fn test_edge_layer_name() -> Result<(), GraphError> { + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None)?; + graph.add_edge(0, 0, 1, NO_PROPS, Some("awesome name"))?; + + test_storage!(&graph, |graph| { + let what = graph.edges().id().collect_vec(); + assert_eq!(what, vec![(0u64.into(), 1u64.into())]); + + let layer_names = graph.edges().layer_names().flatten().sorted().collect_vec(); + assert_eq!(layer_names, vec!["_default", "awesome name"]); + }); + Ok(()) +} + +#[test] +fn test_edge_from_single_layer() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer")).unwrap(); + + test_storage!(&graph, |graph| { + assert!(graph.edge(1, 2).is_some()); + assert!(graph.layers("layer").unwrap().edge(1, 2).is_some()) + }); +} + +#[test] +fn test_edge_layer_intersect_layer() { + let graph = Graph::new(); + + graph + .add_edge(1, 1, 2, NO_PROPS, Some("layer1")) + .expect("add edge"); + graph + .add_edge(1, 1, 3, NO_PROPS, Some("layer3")) + .expect("add edge"); + graph.add_edge(1, 1, 4, NO_PROPS, None).expect("add edge"); + + test_storage!(&graph, |graph| { + let g_layers = graph.layers(vec!["layer1", "layer3"]).expect("layer"); + + assert!(g_layers.layers("layer1").unwrap().edge(1, 2).is_some()); + assert!(g_layers.layers("layer3").unwrap().edge(1, 3).is_some()); + assert!(g_layers.edge(1, 2).is_some()); + assert!(g_layers.edge(1, 3).is_some()); + + assert!(g_layers.edge(1, 4).is_none()); + + let one = g_layers.node(1).expect("node"); + let ns = one + .neighbours() + .iter() + .filter_map(|v| v.id().as_u64()) + .collect::>(); + assert_eq!(ns, vec![2, 3]); + + let g_layers2 = g_layers.layers(vec!["layer1"]).expect("layer"); + + assert!(g_layers2.layers("layer1").unwrap().edge(1, 2).is_some()); + assert!(g_layers2.edge(1, 2).is_some()); + + assert!(g_layers2.edge(1, 3).is_none()); + + assert!(g_layers2.edge(1, 4).is_none()); + + let one = g_layers2.node(1).expect("node"); + let ns = one + .neighbours() + .iter() + .filter_map(|v| v.id().as_u64()) + .collect::>(); + assert_eq!(ns, vec![2]); + }); +} + +#[test] +fn simple_triangle() { + let graph = Graph::new(); + + let vs = vec![(1, 1, 2), (2, 1, 3), (3, 2, 1), (4, 3, 2)]; + + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let windowed_graph = graph.window(0, 5); + let one = windowed_graph.node(1).expect("node"); + let ns_win = one + .neighbours() + .id() + .filter_map(|id| id.to_u64()) + .collect::>(); + + let one = graph.node(1).expect("node"); + let ns = one + .neighbours() + .id() + .filter_map(|id| id.to_u64()) + .collect::>(); + assert_eq!(ns, vec![2, 3]); + assert_eq!(ns_win, ns); + }); +} + +#[test] +fn test_layer_explode() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let e = graph.edge(1, 2).expect("edge"); + + let layer_exploded = e + .explode_layers() + .iter() + .filter_map(|e| { + e.edge + .layer() + .and_then(|layer| Some((e.src().id().as_u64()?, e.dst().id().as_u64()?, layer))) + }) + .collect::>(); + + assert_eq!( + layer_exploded, + vec![ + (1u64, 2u64, LayerId(1)), + (1u64, 2u64, LayerId(2)), + (1u64, 2u64, LayerId(3)), + ] + ); + }); +} + +#[test] +fn test_layer_explode_window() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let g = graph.window(0, 3); + let e = g.edge(1, 2).expect("edge"); + + let layer_exploded = e + .explode_layers() + .iter() + .filter_map(|e| { + e.layer_name() + .ok() + .map(|layer| (e.src().id(), e.dst().id(), layer)) + }) + .collect::>(); + + assert_eq!( + layer_exploded, + vec![ + (GID::U64(1), GID::U64(2), ArcStr::from("layer1")), + (GID::U64(1), GID::U64(2), ArcStr::from("layer2")) + ] + ); + }); +} + +#[test] +fn test_layer_explode_stacking() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let e = graph.edge(1, 2).expect("edge"); + + let layer_exploded = e + .explode_layers() + .iter() + .flat_map(|e| { + e.explode().into_iter().filter_map(|e| { + e.layer_name() + .ok() + .zip(e.time().ok().map(|t| t.t())) + .map(|(layer, t)| (t, e.src().id(), e.dst().id(), layer)) + }) + }) + .collect::>(); + + assert_eq!( + layer_exploded, + vec![ + (0, 1, 2, "layer1"), + (2, 1, 2, "layer1"), + (1, 1, 2, "layer2"), + (3, 1, 2, "_default"), + ] + .into_iter() + .map(|(a, b, c, d)| (a, GID::U64(b), GID::U64(c), ArcStr::from(d))) + .collect::>() + ); + }); +} + +#[test] +fn test_layer_explode_stacking_window() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let g = graph.window(0, 3); + let e = g.edge(1, 2).expect("edge"); + + let layer_exploded = e + .explode_layers() + .iter() + .flat_map(|e| { + e.explode().into_iter().filter_map(|e| { + e.layer_name() + .ok() + .zip(e.time().ok().map(|t| t.t())) + .map(|(layer, t)| (t, e.src().id(), e.dst().id(), layer)) + }) + }) + .collect::>(); + + assert_eq!( + layer_exploded, + vec![ + (0, 1, 2, "layer1"), + (2, 1, 2, "layer1"), + (1, 1, 2, "layer2") + ] + .into_iter() + .map(|(a, b, c, d)| { (a, GID::U64(b), GID::U64(c), ArcStr::from(d)) }) + .collect::>() + ); + }); +} + +#[test] +fn test_multi_layer_degree() { + let graph = Graph::new(); + graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 3, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); + + graph.add_edge(1, 1, 4, NO_PROPS, None).expect("failed"); + graph + .add_edge(1, 1, 2, NO_PROPS, "eth".into()) + .expect("failed"); + graph + .add_edge(1, 1, 3, NO_PROPS, "eth".into()) + .expect("failed"); + + graph + .add_edge(1, 2, 3, NO_PROPS, "eth".into()) + .expect("failed"); + graph.add_edge(1, 4, 3, NO_PROPS, None).expect("failed"); + + test_storage!(&graph, |graph| { + let actual = graph.node(1u64).map(|n| n.out_degree()); + assert_eq!(actual, Some(3)); + + let actual = graph.node(3u64).map(|n| n.in_degree()); + assert_eq!(actual, Some(3)); + + let actual = graph.node(3u64).map(|n| n.degree()); + assert_eq!(actual, Some(3)); + }); +} + +#[test] +fn test_multiple_layers_fundamentals() { + let graph = Graph::new(); + + graph + .add_edge(1, 1, 2, [("tx_sent", 10u64)], "btc".into()) + .expect("failed"); + graph + .add_edge(1, 1, 2, [("tx_sent", 20u64)], "eth".into()) + .expect("failed"); + graph + .add_edge(1, 1, 2, [("tx_sent", 70u64)], "tether".into()) + .expect("failed"); + + test_storage!(&graph, |graph| { + let e = graph.edge(1, 2).expect("failed to get edge"); + let sum: u64 = e + .properties() + .temporal() + .get("tx_sent") + .unwrap() + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum(); + + assert_eq!(sum, 100); + + let lg = graph + .layers(vec!["eth", "btc"]) + .expect("failed to layer graph"); + + let e = lg.edge(1, 2).expect("failed to get edge"); + + let sum_eth_btc: u64 = e + .properties() + .temporal() + .get("tx_sent") + .unwrap() + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum(); + + assert_eq!(sum_eth_btc, 30); + + assert_eq!(lg.count_edges(), 1); + + let e = graph.edge(1, 2).expect("failed to get edge"); + + let e_btc = e.layers("btc").expect("failed to get btc layer"); + let e_eth = e.layers("eth").expect("failed to get eth layer"); + + let edge_btc_sum = e_btc + .properties() + .temporal() + .get("tx_sent") + .unwrap() + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum::(); + + let edge_eth_sum = e_eth + .properties() + .temporal() + .get("tx_sent") + .unwrap() + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum::(); + + assert!(edge_btc_sum < edge_eth_sum); + + let e_eth = e_eth + .layers(vec!["eth", "btc"]) + .expect("failed to get eth,btc layers"); + + let eth_sum = e_eth + .properties() + .temporal() + .get("tx_sent") + .unwrap() + .iter() + .filter_map(|(_, prop)| prop.into_u64()) + .sum::(); + + // layer does not have a way to reset yet! + assert_eq!(eth_sum, 20); + }); +} + +#[test] +fn test_unique_layers() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + + test_storage!(&graph, |graph| { + assert_eq!( + graph + .layers("layer2") + .unwrap() + .unique_layers() + .collect_vec(), + vec!["layer2"] + ) + }); +} + +#[test] +fn node_from_id_is_consistent() { + proptest!(|(nodes: Vec)| { + let g = Graph::new(); + for v in nodes.iter() { + g.add_node(0, *v, NO_PROPS, None, None).unwrap(); + } + prop_assert!(g.nodes() + .name() + .into_iter_values() + .map(|name| g.node(name)) + .all(|v| v.is_some())); + }); +} + +#[test] +fn large_id_is_consistent() { + global_info_logger(); + let g = Graph::new(); + g.add_node(0, 10000000000000000006, NO_PROPS, None, None) + .unwrap(); + info!("names: {:?}", g.nodes().name().collect_vec()); + assert!(g + .nodes() + .name() + .into_iter_values() + .map(|name| g.node(name)) + .all(|v| v.is_some())) +} + +#[test] +fn exploded_edge_times_is_consistent() { + let edges = proptest::collection::vec( + ( + 0u64..100, + 0u64..100, + proptest::collection::vec(-1000i64..1000i64, 1..40), + ), + 1..400, + ); + proptest!(|(edges in edges, offset in -1000i64..1000i64)| { + prop_assert!(check_exploded_edge_times_is_consistent(edges, offset)); + }); +} + +#[test] +fn exploded_edge_times_is_consistent_1() { + let edges = vec![(0, 0, vec![0, 1])]; + assert!(check_exploded_edge_times_is_consistent(edges, 0)); +} + +fn check_exploded_edge_times_is_consistent(edges: Vec<(u64, u64, Vec)>, offset: i64) -> bool { + global_info_logger(); + let mut correct = true; + let mut check = |condition: bool, message: String| { + if !condition { + error!("Failed: {}", message); + } + correct = correct && condition; + }; + // checks that exploded edges are preserved with correct timestamps + let mut edges: Vec<(GID, GID, Vec)> = edges + .into_iter() + .filter_map(|(src, dst, mut ts)| { + ts.sort(); + ts.dedup(); + let ts: Vec<_> = ts.into_iter().filter(|&t| t < i64::MAX).collect(); + (!ts.is_empty()).then_some((GID::U64(src), GID::U64(dst), ts)) + }) + .collect(); + edges.sort(); + edges.dedup_by_key(|(src, dst, _)| src.as_u64().zip(dst.as_u64())); + + let g = Graph::new(); + for (src, dst, times) in edges.iter() { + for &t in times.iter() { + if t < i64::MAX { + g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + } + } + + let mut actual_edges: Vec<(GID, GID, Vec)> = g + .edges() + .iter() + .map(|e| { + ( + e.src().id(), + e.dst().id(), + e.explode() + .iter() + .map(|ee| { + check( + ee.earliest_time() == ee.latest_time(), + format!("times mismatched for {:?}", ee), + ); // times are the same for exploded edge + let t = ee.earliest_time().unwrap().t(); + check( + ee.at(t).is_active(), + format!("exploded edge {:?} inactive at {}", ee, t), + ); + let t_test = t.saturating_add(offset); + if t_test != t && t_test < i64::MAX && t_test > i64::MIN { + check( + !ee.at(t_test).is_active(), + format!("exploded edge {:?} active at {}", ee, t_test), + ); + } + t + }) + .collect(), + ) + }) + .collect(); + + for e in actual_edges.iter_mut() { + e.2.sort(); + } + actual_edges.sort(); + check( + actual_edges == edges, + format!( + "actual edges didn't match input actual: {:?}, expected: {:?}", + actual_edges, edges + ), + ); + correct +} + +#[test] +fn can_apply_algorithm_on_filtered_graph() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, [("layer", 1)], Some("1")).unwrap(); + graph.add_edge(1, 1, 3, [("layer", 1)], Some("1")).unwrap(); + graph.add_edge(1, 2, 3, [("layer", 2)], Some("2")).unwrap(); + graph.add_edge(2, 3, 4, [("layer", 2)], Some("2")).unwrap(); + graph.add_edge(0, 1, 3, [("layer", 2)], Some("2")).unwrap(); + + test_storage!(&graph, |graph| { + let wl = graph.window(0, 3).layers(vec!["1", "2"]).unwrap(); + assert_eq!(weakly_connected_components(&wl).groups().len(), 1); + }); +} + +#[test] +fn test_node_state_merge() { + let graph = Graph::new(); + for i in 0..1_000 { + graph.add_edge(0, i, i + 1, NO_PROPS, None).unwrap(); + } + + let sg = graph.subgraph(1..200); + let degs = degree_centrality(&graph); + let pr = unweighted_page_rank(&sg, None, None, None, false, None); + + let m1 = pr.state.merge( + °s.state, + MergePriority::Left, + MergePriority::Left, + Some(HashMap::from([( + "degree_centrality".to_string(), + MergePriority::Right, + )])), + ); + assert_eq!(m1.values().num_rows(), sg.count_nodes()); + + let m2 = degs.state.merge( + &pr.state, + MergePriority::Left, + MergePriority::Left, + Some(HashMap::from([( + "pagerank_score".to_string(), + MergePriority::Right, + )])), + ); + assert_eq!(m2.values().num_rows(), graph.count_nodes()); +} + +#[test] +#[cfg(feature = "proto")] +fn save_load_serial() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + let dir = tempfile::tempdir().unwrap(); + let file_path = dir.path().join("abcd11"); + g.encode(&file_path).unwrap(); + let gg = Graph::decode(&file_path).unwrap(); + assert_graph_equal(&g, &gg); +} + +#[test] +fn test_node_type_changes() { + let g = Graph::new(); + g.add_node(0, "A", NO_PROPS, Some("typeA"), None).unwrap(); + g.add_node(1, "A", NO_PROPS, None, None).unwrap(); + let node_a = g.node("A").unwrap(); + assert_eq!(node_a.node_type().as_str(), Some("typeA")); + let result = g.add_node(2, "A", NO_PROPS, Some("typeB"), None); + assert!(result.is_err()); +} + +#[test] +fn test_layer_degree() { + let g = Graph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + g.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); + g.add_edge(2, 1, 3, NO_PROPS, Some("layer1")).unwrap(); + g.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&g, |g| { + let n = g.node(1).unwrap(); + let n_layer = n.layers("layer1").unwrap(); + assert_eq!(n_layer.out_degree(), 2); + assert_eq!(n_layer.in_degree(), 0); + assert_eq!(n_layer.degree(), 2); + }); +} + +#[test] +fn test_layer_name() { + let graph = Graph::new(); + + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph + .add_edge(0, 0, 2, NO_PROPS, Some("awesome layer")) + .unwrap(); + + assert_eq!(graph.edge(0, 1).unwrap().layer_names(), ["_default"]); + assert_eq!(graph.edge(0, 2).unwrap().layer_names(), ["awesome layer"]); +} + +#[test] +fn test_type_filter() { + let g = PersistentGraph::new(); + + g.add_node(1, 1, NO_PROPS, Some("wallet"), None).unwrap(); + g.add_node(1, 2, NO_PROPS, Some("timer"), None).unwrap(); + g.add_node(1, 3, NO_PROPS, Some("timer"), None).unwrap(); + g.add_node(1, 4, NO_PROPS, Some("wallet"), None).unwrap(); + + assert_eq!( + g.nodes() + .type_filter(["wallet"]) + .name() + .into_iter_values() + .sorted() + .collect_vec(), + vec!["1", "4"] + ); + + let g = Graph::new(); + g.add_node(1, 1, NO_PROPS, Some("a"), None).unwrap(); + g.add_node(1, 2, NO_PROPS, Some("b"), None).unwrap(); + g.add_node(1, 3, NO_PROPS, Some("b"), None).unwrap(); + g.add_node(1, 4, NO_PROPS, Some("a"), None).unwrap(); + g.add_node(1, 5, NO_PROPS, Some("c"), None).unwrap(); + g.add_node(1, 6, NO_PROPS, Some("e"), None).unwrap(); + g.add_node(1, 7, NO_PROPS, None, None).unwrap(); + g.add_node(1, 8, NO_PROPS, None, None).unwrap(); + g.add_node(1, 9, NO_PROPS, None, None).unwrap(); + g.add_edge(2, 1, 2, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 3, 2, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 2, 4, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 4, 5, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 4, 5, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 5, 6, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 3, 6, NO_PROPS, Some("a")).unwrap(); + + assert_eq!( + g.nodes() + .type_filter(["a", "b", "c", "e"]) + .name() + .sort_by_values(false), + vec!["1", "2", "3", "4", "5", "6"] + ); + + assert_eq!( + g.nodes().type_filter(Vec::::new()).name(), + Vec::::new() + ); + + assert_eq!( + g.nodes().type_filter([""]).name().sort_by_values(false), + vec!["7", "8", "9"] + ); + + let w = g.window(1, 4); + assert_eq!( + w.nodes().type_filter(["a"]).degree().sort_by_id(), + vec![1, 2] + ); + assert_eq!( + w.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c", "b"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + + let l = g.layers(["a"]).unwrap(); + assert_eq!( + l.nodes().type_filter(["a"]).degree().sort_by_id(), + vec![1, 2] + ); + assert_eq!( + l.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c", "b"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + + let sg = g.subgraph([1, 2, 3, 4, 5, 6]); + assert_eq!( + sg.nodes().type_filter(["a"]).degree().sort_by_id(), + vec![1, 2] + ); + assert_eq!( + sg.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c", "b"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect_vec()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + + assert_eq!( + g.nodes().degree().sort_by_id(), + vec![1, 3, 2, 2, 2, 2, 0, 0, 0] + ); + assert_eq!( + g.nodes().type_filter(["a"]).degree().sort_by_id(), + vec![1, 2] + ); + assert_eq!( + g.nodes().type_filter(["d"]).degree().sort_by_id(), + Vec::::new() + ); + assert_eq!( + g.nodes() + .type_filter(["a"]) + .par_iter() + .map(|v| (v, v.degree())) + .collect::>() + .into_iter() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v) + .collect_vec(), + vec![1, 2] + ); + assert_eq!( + g.nodes() + .type_filter(["d"]) + .par_iter() + .map(|v| v.degree()) + .collect::>(), + Vec::::new() + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .collect() + .into_iter() + .map(|n| n.name()) + .collect_vec() + .into_iter() + .sorted() + .collect_vec(), + vec!["1", "4"] + ); + assert_eq!( + g.nodes() + .type_filter(Vec::<&str>::new()) + .collect() + .into_iter() + .map(|n| n.name()) + .collect_vec(), + Vec::<&str>::new() + ); + + assert_eq!(g.nodes().len(), 9); + assert_eq!(g.nodes().type_filter(["b"]).len(), 2); + assert_eq!(g.nodes().type_filter(["d"]).len(), 0); + + assert!(!g.nodes().is_empty()); + assert!(g.nodes().type_filter(["d"]).is_empty()); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .name() + .into_iter_values() + .collect_vec(), + vec!["1", "4"] + ); + assert_eq!( + g.nodes() + .type_filter(["a", "c"]) + .name() + .into_iter_values() + .sorted() + .collect_vec(), + vec!["1", "4", "5"] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + assert_eq!( + g.nodes() + .type_filter(["a", "c"]) + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"], vec!["4", "6"]] + ); + assert_eq!( + g.nodes() + .type_filter(["d"]) + .neighbours() + .name() + .map(|(_, n)| n.collect_vec()) + .collect_vec(), + Vec::>::new() + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.collect::>()) + .collect_vec(), + vec![vec![], vec!["5"]] + ); + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(Vec::<&str>::new()) + .name() + .map(|(_, n)| { n.collect::>() }) + .collect_vec(), + vec![vec![], Vec::<&str>::new()] + ); + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c", "b"]) + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["2"], vec!["2", "5"]] + ); + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["d"]) + .name() + .map(|(_, n)| { n.collect::>() }) + .collect_vec(), + vec![vec![], Vec::<&str>::new()] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec!["1", "3", "4"], vec!["1", "3", "4", "4", "6"]] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["c"]) + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![vec![], vec!["4", "6"]] + ); + + assert_eq!( + g.nodes() + .neighbours() + .neighbours() + .name() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, v)| v.sorted().collect::>()) + .collect_vec(), + vec![ + vec!["1", "3", "4"], + vec!["2", "2", "2", "5", "6"], + vec!["1", "3", "3", "4", "5"], + vec!["1", "3", "4", "4", "6"], + vec!["2", "3", "5", "5"], + vec!["2", "4", "6", "6"], + vec![], + vec![], + vec![], + ] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["d"]) + .total_count(), + 0 + ); + + assert!(g + .nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["d"]) + .is_all_empty()); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["d"]) + .iter() + .map(|(_, n)| { n.name().collect::>() }) + .collect_vec(), + vec![vec![], Vec::<&str>::new()] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["b"]) + .collect() + .into_iter() + .flatten() + .map(|n| n.name()) + .collect_vec(), + vec!["2", "2"] + ); + + assert_eq!( + g.nodes() + .type_filter(["a"]) + .neighbours() + .type_filter(["d"]) + .collect() + .into_iter() + .flatten() + .map(|n| n.name()) + .collect_vec(), + Vec::<&str>::new() + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .name() + .sorted() + .collect_vec(), + vec!["1", "3", "4"] + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["b"]) + .name() + .collect_vec(), + vec!["3"] + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["d"]) + .name() + .collect_vec(), + Vec::<&str>::new() + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["c", "a"]) + .name() + .sorted() + .collect_vec(), + vec!["1", "4"] + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["c"]) + .neighbours() + .name() + .collect_vec(), + Vec::<&str>::new() + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .neighbours() + .name() + .sorted() + .collect_vec(), + vec!["2", "2", "2", "5", "6"], + ); + + assert_eq!( + g.node("2").unwrap().neighbours().type_filter(["d"]).len(), + 0 + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["a"]) + .neighbours() + .len(), + 3 + ); + + assert!(g + .node("2") + .unwrap() + .neighbours() + .type_filter(["d"]) + .is_empty()); + + assert!(!g + .node("2") + .unwrap() + .neighbours() + .type_filter(["a"]) + .neighbours() + .is_empty()); + + assert!(g + .node("2") + .unwrap() + .neighbours() + .type_filter(["d"]) + .neighbours() + .is_empty()); + + assert!(g + .node("2") + .unwrap() + .neighbours() + .type_filter(["d"]) + .iter() + .collect_vec() + .is_empty(),); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["b"]) + .collect() + .into_iter() + .map(|n| n.name()) + .collect_vec(), + vec!["3"] + ); + + assert_eq!( + g.node("2") + .unwrap() + .neighbours() + .type_filter(["d"]) + .collect() + .into_iter() + .map(|n| n.name()) + .collect_vec(), + Vec::<&str>::new() + ); +} + +#[test] +fn test_persistent_graph() { + let g = Graph::new(); + g.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) + .unwrap(); + assert_eq!( + g.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + + let pg = g.persistent_graph(); + pg.delete_edge(10, 0, 1, None).unwrap(); + assert_eq!( + g.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); +} + +#[test] +fn test_unique_property() { + let g = Graph::new(); + g.add_edge(1, 1, 2, [("status", "open")], None).unwrap(); + g.add_edge(2, 1, 2, [("status", "open")], None).unwrap(); + g.add_edge(3, 1, 2, [("status", "review")], None).unwrap(); + g.add_edge(4, 1, 2, [("status", "open")], None).unwrap(); + g.add_edge(5, 1, 2, [("status", "in-progress")], None) + .unwrap(); + g.add_edge(10, 1, 2, [("status", "in-progress")], None) + .unwrap(); + g.add_edge(9, 1, 2, [("state", true)], None).unwrap(); + g.add_edge(10, 1, 2, [("state", false)], None).unwrap(); + g.add_edge(6, 1, 2, NO_PROPS, None).unwrap(); + + let mut props = g + .edge(1, 2) + .unwrap() + .properties() + .temporal() + .get("status") + .unwrap() + .unique() + .into_iter() + .map(|x| x.unwrap_str().to_string()) + .collect_vec(); + props.sort(); + assert_eq!(props, vec!["in-progress", "open", "review"]); + + let ordered_dedupe_latest = g + .edge(1, 2) + .unwrap() + .properties() + .temporal() + .get("status") + .unwrap() + .ordered_dedupe(true) + .into_iter() + .map(|(x, y)| (x.t(), y.unwrap_str().to_string())) + .collect_vec(); + + assert_eq!( + ordered_dedupe_latest, + vec![ + (2, "open".to_string()), + (3, "review".to_string()), + (4, "open".to_string()), + (10, "in-progress".to_string()), + ] + ); + + let ordered_dedupe_earliest = g + .edge(1, 2) + .unwrap() + .properties() + .temporal() + .get("status") + .unwrap() + .ordered_dedupe(false) + .into_iter() + .map(|(x, y)| (x.t(), y.unwrap_str().to_string())) + .collect_vec(); + + assert_eq!( + ordered_dedupe_earliest, + vec![ + (1, "open".to_string()), + (3, "review".to_string()), + (4, "open".to_string()), + (5, "in-progress".to_string()), + ] + ); +} + +#[test] +fn test_create_node() { + let g = Graph::new(); + g.create_node(0, 1, [("test", Prop::Bool(true))], None, None) + .unwrap(); + + let n = g.node(1).unwrap(); + + assert_eq!(n.id().as_u64().unwrap(), 1); + assert_eq!(n.properties().get("test").unwrap(), Prop::Bool(true)); + + let result = g.create_node(1, 1, [("test".to_string(), Prop::Bool(true))], None, None); + assert!(matches!(result, Err(GraphError::NodeExistsError(id)) if id == GID::U64(1))); +} + +#[test] +fn test_materialize_edge_metadata() { + let g = Graph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.add_metadata([("test", "test")], None).unwrap(); + g.add_edge(10, 1, 2, NO_PROPS, Some("a")).unwrap(); + + let gw = g.after(1); + let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); +} + +#[test] +fn test_id_filter() { + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + assert_eq!(graph.nodes().id(), [0, 1]); + assert_eq!(graph.nodes().id_filter([0]).len(), 1); + assert_eq!(graph.nodes().id_filter([0]).id(), [0]); + assert_eq!(graph.nodes().id_filter([0]).degree(), [1]); +} + +#[test] +fn test_indexed() { + proptest!(|(graph in build_graph_strat(10, 10, 10, 10, false), nodes in subsequence((0..10).collect::>(), 0..10))| { + let graph = Graph::from(build_graph(&graph)); + let expected_node_ids = nodes.iter().copied().filter(|&id| graph.has_node(id)).collect::>(); + let nodes = graph.nodes().id_filter(nodes); + assert_eq!(nodes.id(), expected_node_ids); + }) +} + +#[test] +fn materialize_window_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = Graph::from(build_graph(&graph_f)); + let gw = g.window(w.start, w.end); + let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); + }) +} + +#[test] +fn materialize_temporal_properties_one_edge() { + let g = Graph::new(); + g.add_edge( + 0, + 0, + 0, + [("3", Prop::I64(1)), ("0", Prop::str("baa"))], + Some("a"), + ) + .unwrap(); + + let gw = g.window(-9, 3); + let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); +} + +#[test] +fn materialize_one_node() { + let g = Graph::new(); + g.add_node(0, 0, NO_PROPS, None, None).unwrap(); + + let n = g.node(0).unwrap(); + let hist = n.history(); + assert!(!hist.is_empty()); + let rows = n.rows().collect::>(); + assert!(!rows.is_empty()); + + let gw = g.window(0, 1); + let gmw = gw.materialize().unwrap(); + + assert_graph_equal(&gw, &gmw); +} + +#[test] +fn materialize_some_edges() -> Result<(), GraphError> { + let edges1_props = EdgeUpdatesFixture { + props: PropUpdatesFixture { + t_props: vec![ + (2433054617899119663, vec![]), + ( + 5623371002478468619, + vec![("0".to_owned(), Prop::I64(-180204069376666762))], + ), + ], + c_props: vec![], + }, + deletions: vec![-3684372592923241629, 3668280323305195349], + }; + + let edges2_props = EdgeUpdatesFixture { + props: PropUpdatesFixture { + t_props: vec![ + ( + -7888823724540213280, + vec![("0".to_owned(), Prop::I64(1339447446033500001))], + ), + (-3792330935693192039, vec![]), + ( + 4049942931077033460, + vec![("0".to_owned(), Prop::I64(-544773539725842277))], + ), + (5085404190610173488, vec![]), + (1445770503123270290, vec![]), + (-5628624083683143619, vec![]), + (-394401628579820652, vec![]), + (-2398199704888544233, vec![]), + ], + c_props: vec![("0".to_owned(), Prop::I64(-1877019573933389749))], + }, + deletions: vec![ + 3969804007878301015, + 7040207277685112004, + 7380699292468575143, + 3332576590029503186, + -1107894292705275349, + 6647229517972286485, + 6359226207899406831, + ], + }; + + let edges: EdgeFixture = [ + ((2, 7, Some("b")), edges1_props), + ((7, 2, Some("a")), edges2_props), + ] + .into_iter() + .collect(); + + let w = -3619743214445905380..90323088878877991; + let graph_f = GraphFixture { + nodes: NodeFixture::default(), + edges, + }; + + let g = Graph::from(build_graph(&graph_f)); + let gw = g.window(w.start, w.end); + let gmw = gw.materialize()?; + assert_graph_equal(&gw, &gmw); + + Ok(()) +} + +#[test] +fn materialize_window_delete_test() { + let g = Graph::new(); + g.delete_edge(0, 0, 0, Some("a")).unwrap(); + let w = 0..1; + let gw = g.window(w.start, w.end); + let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); +} + +#[test] +fn test_multilayer() { + let g = Graph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(1, 0, 0, NO_PROPS, Some("a")).unwrap(); + let gw = g.window(0, 1); + + let expected = Graph::new(); + expected.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + expected.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gw, &expected); +} + +#[test] +fn test_empty_window() { + let g = Graph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + let gw = g.window(-1, 0); + + assert!(g.window(-1, 0).nodes().is_empty()); + assert_eq!(g.window(-1, 0).count_nodes(), 0); + for layer in gw.unique_layers() { + let layered = gw.valid_layers(layer); + assert_eq!(layered.count_nodes(), 0); + } +} + +#[test] +fn add_edge_and_read_props_concurrent() { + for t in 0..1000 { + let g = Graph::new(); + join( + || g.add_edge(t, 1, 2, [("test", true)], None).unwrap(), + || { + // if the edge exists already, it should have the property set + g.window(t, t + 1) + .edge(1, 2) + .map(|e| assert!(e.properties().get("test").is_some())) + }, + ); + } +} + +#[test] +fn test_group_by() { + let g = Graph::new(); + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); + g.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); + let groups_from_lazy = g.nodes().out_degree().groups(); + + let groups_from_eager = g.nodes().out_degree().compute().groups(); + + let expected_groups: HashMap> = HashMap::from([ + (0, Arc::from_iter([GID::U64(3), GID::U64(5)])), + (1, Arc::from_iter([GID::U64(1), GID::U64(2), GID::U64(4)])), + ]); + + let expected_subgraphs: HashMap> = HashMap::from([ + (0, Arc::from_iter([])), + (1, Arc::from_iter([GID::U64(1), GID::U64(2)])), + ]); + + assert_eq!( + groups_from_lazy + .iter() + .map(|(v, nodes)| (*v, nodes.id().sort_by_values(false).values().clone())) + .collect::>(), + expected_groups + ); + + assert_eq!( + groups_from_lazy + .clone() + .into_iter_groups() + .map(|(v, nodes)| (v, nodes.id().sort_by_values(false).values().clone())) + .collect::>(), + expected_groups + ); + + assert_eq!( + groups_from_lazy + .iter_subgraphs() + .map(|(v, graph)| ( + *v, + graph.nodes().id().sort_by_values(false).values().clone() + )) + .collect::>(), + expected_subgraphs + ); + + assert_eq!( + groups_from_lazy + .clone() + .into_iter_subgraphs() + .map(|(v, graph)| (v, graph.nodes().id().sort_by_values(false).values().clone())) + .collect::>(), + expected_subgraphs + ); + + assert_eq!( + groups_from_eager + .iter() + .map(|(v, nodes)| (*v, nodes.id().sort_by_values(false).values().clone())) + .collect::>(), + expected_groups + ); + + assert_eq!(groups_from_lazy.len(), expected_groups.len()); + + for (i, (v, nodes)) in groups_from_eager.iter().enumerate() { + let (v2, nodes2) = groups_from_eager.group(i).unwrap(); + assert_eq!(v, v2); + assert!(nodes.iter().eq(nodes2.iter())); + let (v3, graph) = groups_from_eager.group_subgraph(i).unwrap(); + assert_eq!(v, v3); + assert_eq!( + graph.nodes().id().sort_by_values(false), + expected_subgraphs[v].deref() + ); + } +} diff --git a/raphtory/tests/deletion_graph.rs b/raphtory-test-utils/tests/deletion_graph.rs similarity index 100% rename from raphtory/tests/deletion_graph.rs rename to raphtory-test-utils/tests/deletion_graph.rs diff --git a/raphtory/tests/df_loaders.rs b/raphtory-test-utils/tests/df_loaders.rs similarity index 98% rename from raphtory/tests/df_loaders.rs rename to raphtory-test-utils/tests/df_loaders.rs index dff88b95d3..cfba2daa4b 100644 --- a/raphtory/tests/df_loaders.rs +++ b/raphtory-test-utils/tests/df_loaders.rs @@ -1,4 +1,4 @@ -#[cfg(all(test, feature = "test-utils", feature = "io"))] +#[cfg(test)] mod io_tests { use arrow::array::builder::{ ArrayBuilder, Int64Builder, LargeStringBuilder, StringViewBuilder, UInt64Builder, @@ -13,17 +13,19 @@ mod io_tests { nodes::{load_node_props_from_df, load_nodes_from_df}, }, }, + core::storage::timeindex::EventTime, db::graph::graph::assert_graph_equal, errors::GraphError, prelude::*, - test_utils::{build_edge_list, build_edge_list_str, build_edge_list_with_secondary_index}, }; use raphtory_api::core::{entities::LayerIds, storage::arc_str::ArcStr}; - use raphtory_core::storage::timeindex::EventTime; use raphtory_storage::{ core_ops::CoreGraphOps, mutation::addition_ops::{InternalAdditionOps, SessionAdditionOps}, }; + use raphtory_test_utils::test_utils::{ + build_edge_list, build_edge_list_str, build_edge_list_with_secondary_index, + }; fn build_df( chunk_size: usize, @@ -776,7 +778,7 @@ mod io_tests { } } -#[cfg(all(test, feature = "test-utils", feature = "io"))] +#[cfg(test)] mod parquet_tests { use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; @@ -787,11 +789,11 @@ mod parquet_tests { views::deletion_graph::PersistentGraph, }, prelude::*, - test_utils::{ - assert_valid_graph, build_edge_list_dyn, build_graph, build_graph_strat, - build_nodes_dyn, build_props_dyn, EdgeFixture, EdgeUpdatesFixture, GraphFixture, - NodeFixture, NodeUpdatesFixture, PropUpdatesFixture, - }, + }; + use raphtory_test_utils::test_utils::{ + assert_valid_graph, build_edge_list_dyn, build_graph, build_graph_strat, build_nodes_dyn, + build_props_dyn, EdgeFixture, EdgeUpdatesFixture, GraphFixture, NodeFixture, + NodeUpdatesFixture, PropUpdatesFixture, }; use serde_json::json; use std::{io::Cursor, str::FromStr}; diff --git a/raphtory-test-utils/tests/edge_property_filter.rs b/raphtory-test-utils/tests/edge_property_filter.rs new file mode 100644 index 0000000000..dfaf958e86 --- /dev/null +++ b/raphtory-test-utils/tests/edge_property_filter.rs @@ -0,0 +1,381 @@ +use itertools::Itertools; +use proptest::{arbitrary::any, proptest}; +use raphtory::{ + db::{ + api::view::Filter, + graph::{ + graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, + views::{ + deletion_graph::PersistentGraph, + filter::model::{ + node_filter::ops::NodeFilterOps, property_filter::ops::PropertyFilterOps, + ComposableFilter, EdgeFilter, PropertyFilterFactory, + }, + }, + }, + }, + prelude::*, +}; +use raphtory_api::core::{entities::properties::prop::PropType, storage::timeindex::AsTime}; +use raphtory_storage::mutation::addition_ops::{InternalAdditionOps, SessionAdditionOps}; +use raphtory_test_utils::{ + assertions::{assert_ok_or_missing_edges, EdgeRow}, + test_utils::{build_edge_deletions, build_edge_list, build_graph_from_edge_list, build_window}, +}; + +#[test] +fn test_edge_filter() { + let g = Graph::new(); + g.add_edge(0, "Jimi", "John", [("band", "JH Experience")], None) + .unwrap(); + g.add_edge(1, "John", "David", [("band", "Dead & Company")], None) + .unwrap(); + g.add_edge(2, "David", "Jimi", [("band", "Pink Floyd")], None) + .unwrap(); + + let filter_expr = EdgeFilter::dst() + .name() + .eq("David") + .and(EdgeFilter.property("band").eq("Dead & Company")); + let filtered_edges = g.filter(filter_expr).unwrap(); + + assert_eq!( + filtered_edges + .edges() + .iter() + .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) + .collect::>(), + vec!["John->David"] + ); + + let g_expected = Graph::new(); + g_expected + .add_edge((1, 1), "John", "David", [("band", "Dead & Company")], None) + .unwrap(); + + assert_graph_equal(&filtered_edges, &g_expected); +} + +#[test] +fn test_edge_filter_persistent() { + let g = PersistentGraph::new(); + g.add_edge(0, "Jimi", "John", [("band", "JH Experience")], None) + .unwrap(); + g.add_edge(1, "John", "David", [("band", "Dead & Company")], None) + .unwrap(); + g.add_edge(2, "David", "Jimi", [("band", "Pink Floyd")], None) + .unwrap(); + + let filter_expr = EdgeFilter::dst() + .name() + .eq("David") + .and(EdgeFilter.property("band").eq("Dead & Company")); + let filtered_edges = g.filter(filter_expr).unwrap(); + + let g_expected = PersistentGraph::new(); + g_expected + .add_edge((1, 1), "John", "David", [("band", "Dead & Company")], None) + .unwrap(); + + assert_eq!( + filtered_edges + .edges() + .iter() + .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) + .collect::>(), + vec!["John->David"] + ); + assert_graph_equal(&filtered_edges, &g_expected); +} + +#[test] +fn test_edge_property_filter_on_nodes() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("test", 1i64)], None).unwrap(); + g.add_edge(0, 1, 3, [("test", 3i64)], None).unwrap(); + g.add_edge(1, 2, 3, [("test", 2i64)], None).unwrap(); + g.add_edge(1, 2, 4, [("test", 0i64)], None).unwrap(); + + let filter = EdgeFilter.property("test").eq(1i64); + let n1 = g.node(1).unwrap().filter(filter).unwrap(); + assert_eq!( + n1.edges().id().collect_vec(), + vec![(GID::U64(1), GID::U64(2))] + ); + let n2 = g + .node(2) + .unwrap() + .filter(EdgeFilter.property("test").gt(1i64)) + .unwrap(); + assert_eq!( + n2.edges().id().collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); +} + +#[test] +fn test_filter() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("test", 1i64)], None).unwrap(); + g.add_edge(1, 2, 3, [("test", 2i64)], None).unwrap(); + + let filter = EdgeFilter.property("test").eq(1i64); + let gf = g.filter(filter).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(1), GID::U64(2))] + ); + let gf = g.filter(EdgeFilter.property("test").gt(1i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); +} + +#[test] +fn test_filter_gt() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() > v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } + } + }); + }) +} + +#[test] +fn test_filter_ge() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").ge(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() >= v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } + } + }); + }) +} + +#[test] +fn test_filter_lt() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").lt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() < v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } + } + }); + }) +} + +#[test] +fn test_filter_le() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").le(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() <= v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } + } + }); + }) +} + +#[test] +fn test_filter_eq() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() == v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } + } + }); + }) +} + +#[test] +fn test_filter_ne() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = EdgeFilter.property("int_prop").ne(v); + assert_ok_or_missing_edges(&edges, g.filter(filter), |filtered| { + for e in g.edges().iter() { + if e.properties().get("int_prop").unwrap_i64() != v { + assert!(filtered.has_edge(e.src(), e.dst())); + } else { + assert!(!filtered.has_edge(e.src(), e.dst())); + } + } + }); + }) +} + +#[test] +fn test_graph_materialise_window() { + proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { + let g = build_graph_from_edge_list(&edges); + for (src, dst, t) in edge_deletions { + g.delete_edge(t, src, dst, None).unwrap(); + } + let filter = EdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.window(start, end).filter(filter.clone()), |filtered| { + let gwfm = filtered.materialize().unwrap(); + assert_graph_equal(&filtered, &gwfm); + }); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let gwf = filtered.window(start, end); + let gwfm = gwf.materialize().unwrap(); + assert_graph_equal(&gwf, &gwfm); + }); + }) +} + +fn check_persistent_graph_mat_window( + edges: &[EdgeRow], + edge_deletions: Vec<(u64, u64, i64)>, + v: i64, + (start, end): (i64, i64), +) { + let g = build_graph_from_edge_list(edges); + let g = g.persistent_graph(); + for (src, dst, t) in edge_deletions { + g.delete_edge(t, src, dst, None).unwrap(); + } + let filter = EdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges( + edges, + g.window(start, end).filter(filter.clone()), + |filtered| { + let gwfm = filtered.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&filtered, &gwfm); + }, + ); + assert_ok_or_missing_edges(edges, g.filter(filter.clone()), |filtered| { + let gfw = filtered.window(start, end); + let gfwm = gfw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gfw, &gfwm); + }); +} + +#[test] +fn test_persistent_graph_materialise_window() { + proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { + check_persistent_graph_mat_window(&edges, edge_deletions, v, (start, end)); + }) +} + +#[test] +fn simplte_graph_materialize_window() { + let edges = [(0, 0, 0, "".to_owned(), 0), (0, 0, 0, "".to_owned(), 0)]; + let edge_deletions = vec![]; + let start_end = (1, 2); + let v = -1; + check_persistent_graph_mat_window(&edges, edge_deletions, v, start_end); +} + +#[test] +fn test_single_unfiltered_edge_empty_window_persistent() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.delete_edge(10, 0, 1, None).unwrap(); + let gw = g + .filter(EdgeFilter.property("test").gt(0i64)) + .unwrap() + .window(0, 0); + + assert_eq!(gw.count_edges(), 0); + let expected = PersistentGraph::new(); + expected + .write_session() + .unwrap() + .resolve_edge_property("test", PropType::I64, false) + .unwrap(); + expected.resolve_layer(None).unwrap(); + assert_graph_equal(&gw, &expected) +} + +#[test] +fn test_single_deleted_edge_window_persistent() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.delete_edge(1, 0, 1, None).unwrap(); + let gw = g + .filter(EdgeFilter.property("test").gt(0i64)) + .unwrap() + .window(0, 2); + let gm = gw.materialize().unwrap(); + + assert_eq!(gw.count_edges(), 1); + assert_eq!(gw.count_temporal_edges(), 1); + + assert_eq!(gw.node(0).unwrap().edge_history_count(), 2); + assert_eq!(gw.node(0).unwrap().after(0).edge_history_count(), 1); + + assert_persistent_materialize_graph_equal(&gw, &gm) +} + +#[test] +fn test_single_unfiltered_edge_window_persistent_2() { + let g = PersistentGraph::new(); + g.add_edge(1, 0, 1, [("test", 1i64)], None).unwrap(); + g.delete_edge(0, 0, 0, None).unwrap(); + + let gwf = g + .window(-1, 2) + .filter(EdgeFilter.property("test").gt(0i64)) + .unwrap(); + assert!(gwf.has_edge(0, 1)); + assert!(!gwf.has_edge(0, 0)); + assert_eq!(gwf.node(0).unwrap().earliest_time().map(|t| t.t()), Some(1)); + assert_persistent_materialize_graph_equal(&gwf, &gwf.materialize().unwrap()); + + let gfw = g + .filter(EdgeFilter.property("test").gt(0i64)) + .unwrap() + .window(-1, 2); + let gm = gfw.materialize().unwrap(); + + assert_eq!(gfw.count_edges(), 1); + assert_eq!(gfw.count_temporal_edges(), 1); + + assert_eq!(gfw.node(0).unwrap().edge_history_count(), 1); + assert_eq!(gfw.node(0).unwrap().after(0).edge_history_count(), 1); + + assert_persistent_materialize_graph_equal(&gfw, &gm) +} diff --git a/raphtory-test-utils/tests/exploded_edge_property_filter.rs b/raphtory-test-utils/tests/exploded_edge_property_filter.rs new file mode 100644 index 0000000000..03bccb2a30 --- /dev/null +++ b/raphtory-test-utils/tests/exploded_edge_property_filter.rs @@ -0,0 +1,677 @@ +use itertools::Itertools; +use proptest::{arbitrary::any, proptest}; +use raphtory::{ + core::entities::nodes::node_ref::AsNodeRef, + db::{ + api::view::{Filter, StaticGraphViewOps}, + graph::{ + edge::EdgeView, + graph::{ + assert_graph_equal, assert_node_equal, assert_nodes_equal, + assert_persistent_materialize_graph_equal, + }, + views::{ + deletion_graph::PersistentGraph, + filter::model::{ + property_filter::ops::PropertyFilterOps, ExplodedEdgeFilter, + PropertyFilterFactory, + }, + }, + }, + }, + prelude::*, +}; +use raphtory_api::core::{ + entities::properties::prop::PropType, + storage::{arc_str::ArcStr, timeindex::AsTime}, +}; +use raphtory_storage::{ + core_ops::CoreGraphOps, + mutation::addition_ops::{InternalAdditionOps, SessionAdditionOps}, +}; +use raphtory_test_utils::{ + assertions::assert_ok_or_missing_edges, + test_utils::{ + build_edge_deletions, build_edge_list, build_edge_list_with_deletions, + build_graph_from_edge_list, build_window, Update, + }, +}; +use std::collections::HashMap; + +fn build_filtered_graph( + edges: &[(u64, u64, i64, String, i64)], + filter: impl Fn(i64) -> bool, +) -> Graph { + let g = Graph::new(); + for (index, (src, dst, t, str_prop, int_prop)) in edges.iter().enumerate() { + if filter(*int_prop) { + g.add_edge( + (*t, index), + *src, + *dst, + [ + ("str_prop", Prop::str(str_prop.as_ref())), + ("int_prop", Prop::I64(*int_prop)), + ], + None, + ) + .unwrap(); + } + } + if !edges.is_empty() { + g.resolve_layer(None).unwrap(); + } + g +} + +fn build_filtered_nodes_graph( + edges: &[(u64, u64, i64, String, i64)], + filter: impl Fn(i64) -> bool, +) -> Graph { + let g = Graph::new(); + for (src, dst, t, str_prop, int_prop) in edges { + if filter(*int_prop) { + g.add_edge( + *t, + *src, + *dst, + [ + ("str_prop", str_prop.as_str().into()), + ("int_prop", Prop::I64(*int_prop)), + ], + None, + ) + .unwrap(); + } + g.atomic_add_node(src.as_node_ref()).unwrap(); + g.atomic_add_node(dst.as_node_ref()).unwrap(); + } + if !edges.is_empty() { + g.resolve_layer(None).unwrap(); + } + g +} + +fn build_filtered_persistent_graph( + edges: HashMap<(u64, u64), Vec<(i64, Update)>>, + filter: impl Fn(i64) -> bool, +) -> (PersistentGraph, PersistentGraph) { + let g = PersistentGraph::new(); + let g_filtered = PersistentGraph::new(); + if !edges.iter().all(|(_, v)| v.is_empty()) { + g_filtered.resolve_layer(None).unwrap(); + } + for ((src, dst), updates) in edges { + for (t, update) in updates { + match update { + Update::Deletion => { + g.delete_edge(t, src, dst, None).unwrap(); + g_filtered.delete_edge(t, src, dst, None).unwrap(); + } + Update::Addition(str_prop, int_prop) => { + g.add_edge( + t, + src, + dst, + [ + ("str_prop", str_prop.clone().into()), + ("int_prop", Prop::I64(int_prop)), + ], + None, + ) + .unwrap(); + if filter(int_prop) { + g_filtered + .add_edge( + t, + src, + dst, + [ + ("str_prop", str_prop.into()), + ("int_prop", Prop::I64(int_prop)), + ], + None, + ) + .unwrap(); + } else { + g_filtered.delete_edge(t, src, dst, None).unwrap(); + // properties still exist after filtering + let session = g_filtered.write_session().unwrap(); + session + .resolve_edge_property("str_prop", PropType::Str, false) + .unwrap(); + session + .resolve_edge_property("int_prop", PropType::I64, false) + .unwrap(); + } + } + } + } + } + (g, g_filtered) +} + +fn edge_attr( + e: &EdgeView, +) -> (String, String, Option, Option) { + let src = e.src().name(); + let dst = e.dst().name(); + let int_prop = e.properties().get("int_prop"); + let str_prop = e.properties().get("str_prop"); + (src, dst, int_prop.into_i64(), str_prop.into_str()) +} + +#[test] +fn test_filter_gt() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv > v); + assert_graph_equal(&filtered, &expected_filtered_g); + #[cfg(feature = "search")] + { + let search_ee = g.search_exploded_edges(filter, 100, 0).unwrap(); + let filter_ee = filtered.edges().explode().collect(); + let from_search = search_ee.iter().map(edge_attr).collect_vec(); + let from_filter = filter_ee.iter().map(edge_attr).collect_vec(); + assert_eq!(from_search, from_filter); + } + }); + }) +} + +#[test] +fn test_filter_gt_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv > v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").gt(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + assert_graph_equal(&filtered, &expected_filtered_g); + } else { + assert!(filtered.is_err()) + } + }) +} + +#[test] +fn test_one_edge() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("int_prop", 0i64)], None).unwrap(); + let filtered = g + .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) + .unwrap(); + let gf = Graph::new(); + gf.resolve_layer(None).unwrap(); + assert_eq!(filtered.count_nodes(), 0); + assert_eq!(filtered.count_edges(), 0); + assert_graph_equal(&filtered, &gf); +} + +#[test] +fn test_filter_ge() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").ge(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv >= v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) +} + +#[test] +fn test_filter_ge_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv >= v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + if p.is_some() { + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").ge(v) + ).unwrap(); + assert_graph_equal(&filtered, &expected_filtered_g); + } + + }) +} + +#[test] +fn test_filter_persistent_single_filtered_edge() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, [("test", 1i64)], None).unwrap(); + let gf = g + .filter(ExplodedEdgeFilter.property("test").gt(1i64)) + .unwrap(); + + assert_eq!(gf.count_edges(), 1); + assert_eq!(gf.count_temporal_edges(), 0); + + let expected = PersistentGraph::new(); + expected.delete_edge(0, 0, 0, None).unwrap(); + //the property still exists! + expected + .write_session() + .unwrap() + .resolve_edge_property("test", PropType::I64, false) + .unwrap(); + + assert_graph_equal(&gf, &expected); +} + +#[test] +fn test_filter_lt() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").lt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv < v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) +} + +#[test] +fn test_filter_lt_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv < v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").lt(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + assert_graph_equal(&filtered, &expected_filtered_g); + } else { + assert!(filtered.is_err()) + } + }) +} + +#[test] +fn test_filter_le() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").le(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv <= v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) +} + +#[test] +fn test_filter_le_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv <= v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").le(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + assert_graph_equal(&filtered, &expected_filtered_g); + } else { + assert!(filtered.is_err()) + } + }) +} + +#[test] +fn test_filter_eq() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) +} + +#[test] +fn test_filter_eq_one_edge() { + let g = Graph::new(); + g.add_edge(0, 0, 0, [("int_prop", Prop::I64(0))], None) + .unwrap(); + let filter = ExplodedEdgeFilter.property("int_prop").eq(0i64); + assert_graph_equal(&g.filter(filter.clone()).unwrap(), &g); +} + +#[test] +fn test_filter_eq_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv == v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").eq(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + assert_graph_equal(&filtered, &expected_filtered_g); + } else { + assert!(filtered.is_err()) + } + }) +} + +#[test] +fn test_filter_ne() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").ne(v); + assert_ok_or_missing_edges(&edges, g.filter(filter), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv != v); + assert_graph_equal(&filtered, &expected_filtered_g); + }); + }) +} + +#[test] +fn test_filter_ne_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv != v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").ne(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + assert_graph_equal(&filtered, &expected_filtered_g); + } else { + assert!(filtered.is_err()) + } + }) +} + +#[test] +fn test_filter_window() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::(), (start, end) in build_window() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); + assert_graph_equal(&filtered.window(start, end), &expected_filtered_g.window(start, end)); + }); + }); +} + +#[test] +fn test_filter_window_persistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::(), (start, end) in build_window() + )| { + let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv >= v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").ge(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + assert_graph_equal(&filtered.window(start, end), &expected_filtered_g.window(start, end)); + } else { + assert!(filtered.is_err()) + } + }) +} + +#[test] +fn test_filter_materialise_is_consistent() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let mat = filtered.materialize().unwrap(); + assert_graph_equal(&filtered, &mat); + }); + }) +} + +#[test] +fn test_filter_persistent_materialise_is_consistent() { + proptest!(|( + edges in build_edge_list_with_deletions(100, 100), v in any::() + )| { + let (g, expected) = build_filtered_persistent_graph(edges, |vv| vv >= v); + let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); + let filtered = g.filter( + ExplodedEdgeFilter.property("int_prop").ge(v) + ); + if p.is_some() { + let filtered = filtered.unwrap(); + let mat = filtered.materialize().unwrap(); + assert_graph_equal(&filtered, &mat); + assert_graph_equal(&expected, &mat); + } else { + assert!(filtered.is_err()) + } + }) +} + +#[test] +#[ignore = "need a way to add a node without timestamp"] +fn test_filter_on_nodes() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.nodes().filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_nodes_graph(&edges, |vv| vv == v); + assert_nodes_equal(&filtered, &expected_filtered_g.nodes()); + }); + }) +} + +#[test] +#[ignore = "need a way to add a node without timestamp"] +fn test_filter_on_nodes_simple() { + let edges = [(1u64, 2u64, 0i64, "a".to_string(), 10i64)]; + let v = -1; + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.nodes().filter(filter.clone()), |filtered| { + let expected_filtered_g = build_filtered_nodes_graph(&edges, |vv| vv == v); + assert_nodes_equal(&filtered, &expected_filtered_g.nodes()); + }); +} + +#[test] +fn test_filter_on_node() { + proptest!(|( + edges in build_edge_list(100, 2), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + if let Some(node) = g.node(0) { + let filtered_node = node.filter( + ExplodedEdgeFilter.property("int_prop").eq(v) + ).unwrap(); + let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); + if filtered_node.degree() == 0 { + // should be filtered out + assert!(expected_filtered_g.node(0).is_none()) + } else { + assert_node_equal(filtered_node, expected_filtered_g.node(0).unwrap()) + } + } + }) +} + +#[test] +fn test_filter_materialise_window_is_consistent() { + proptest!(|( + edges in build_edge_list(100, 100), v in any::(), (start, end) in build_window() + )| { + let g = build_graph_from_edge_list(&edges); + let filter = ExplodedEdgeFilter.property("int_prop").eq(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let left = filtered.window(start, end); + let right = filtered.window(start, end).materialize().unwrap(); + assert_graph_equal(&left, &right); + }); + }) +} + +#[test] +fn test_persistent_graph() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, [("int_prop", 0i64)], None).unwrap(); + g.delete_edge(2, 1, 2, None).unwrap(); + g.add_edge(5, 1, 2, [("int_prop", 5i64)], None).unwrap(); + g.delete_edge(7, 1, 2, None).unwrap(); + + let edges = g + .node(1) + .unwrap() + .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) + .unwrap() + .edges() + .explode() + .collect(); + println!("{:?}", edges); + + assert_eq!(edges.len(), 1); + let gf = g + .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) + .unwrap(); + let gfm = gf.materialize().unwrap(); + + assert_graph_equal(&gf, &gfm); // check materialise is consistent +} + +#[test] +fn test_persistent_graph_explode_semantics() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 0i64)], None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.add_edge(5, 0, 1, [("test", 5i64)], None).unwrap(); + g.delete_edge(10, 0, 1, None).unwrap(); + + let gf = g + .filter(ExplodedEdgeFilter.property("test").ne(2i64)) + .unwrap(); + assert_eq!( + gf.edges() + .explode() + .earliest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(0i64), Some(5i64)] + ); + assert_eq!( + gf.edges() + .explode() + .latest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(2i64), Some(10i64)] + ); +} + +#[test] +fn test_persistent_graph_materialise() { + proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::())| { + let g = build_graph_from_edge_list(&edges); + let g = g.persistent_graph(); + for (src, dst, t) in edge_deletions { + g.delete_edge(t, src, dst, None).unwrap(); + } + let filter = ExplodedEdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let gfm = filtered.materialize().unwrap(); + assert_graph_equal(&filtered, &gfm) + }); + }) +} + +#[test] +fn test_single_filtered_edge_persistent() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, [("test", 0i64)], None).unwrap(); + let gf = g + .filter(ExplodedEdgeFilter.property("test").gt(0i64)) + .unwrap(); + let gfm = gf.materialize().unwrap(); + dbg!(&gfm); + assert_eq!(gf.node(0).unwrap().out_degree(), 1); + assert_eq!(gfm.node(0).unwrap().out_degree(), 1); + assert_graph_equal(&gf, &gfm); +} + +#[test] +fn test_persistent_graph_materialise_window() { + proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { + let g = build_graph_from_edge_list(&edges); + let g = g.persistent_graph(); + for (src, dst, t) in edge_deletions { + g.delete_edge(t, src, dst, None).unwrap(); + } + let filter = ExplodedEdgeFilter.property("int_prop").gt(v); + assert_ok_or_missing_edges(&edges, g.window(start, end).filter(filter.clone()), |filtered| { + let gwfm = filtered.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&filtered, &gwfm); + }); + assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { + let gfw = filtered.window(start, end); + let gfwm = gfw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gfw, &gfwm); + }); + }) +} + +#[test] +fn test_persistent_failure() { + let g = PersistentGraph::new(); + g.add_edge(-1, 0, 1, [("test", Prop::I32(-1))], None) + .unwrap(); + g.add_edge(0, 0, 1, [("test", Prop::I32(1))], None).unwrap(); + + let gwf = g + .filter(ExplodedEdgeFilter.property("test").gt(0)) + .unwrap() + .window(-1, 0); + assert_eq!(gwf.count_nodes(), 0); + assert_eq!(gwf.count_edges(), 0); + let gm = gwf.materialize().unwrap(); + + assert_persistent_materialize_graph_equal(&gwf, &gm); + + let gfw = g + .window(-1, 0) + .filter(ExplodedEdgeFilter.property("test").gt(0)) + .unwrap(); + assert_eq!(gfw.count_edges(), 0); + let gm = gfw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gfw, &gm); +} diff --git a/raphtory-test-utils/tests/node_property_filter.rs b/raphtory-test-utils/tests/node_property_filter.rs new file mode 100644 index 0000000000..966f6e4a74 --- /dev/null +++ b/raphtory-test-utils/tests/node_property_filter.rs @@ -0,0 +1,458 @@ +use itertools::Itertools; +use proptest::{arbitrary::any, proptest}; +use raphtory::{ + db::{ + api::view::filter_ops::{Filter, NodeSelect}, + graph::{ + graph::assert_edges_equal, + views::filter::model::{ + node_filter::{ops::NodeFilterOps, NodeFilter}, + property_filter::ops::PropertyFilterOps, + ComposableFilter, PropertyFilterFactory, + }, + }, + }, + prelude::*, +}; +use raphtory_test_utils::{ + assertions::assert_ok_or_missing_nodes, + test_utils::{ + add_node_props, build_edge_list, build_graph_from_edge_list, build_node_props, + node_filtered_graph, + }, +}; + +#[test] +#[ignore] +// TODO: Enable this once fixed +fn test_node_filter_on_nodes() { + let g = Graph::new(); + g.add_node(0, "Jimi", [("band", "JH Experience")], None, None) + .unwrap(); + g.add_node(1, "John", [("band", "Dead & Company")], None, None) + .unwrap(); + g.add_node(2, "David", [("band", "Pink Floyd")], None, None) + .unwrap(); + + let filter_expr = NodeFilter::name() + .eq("John") + .and(NodeFilter.property("band").eq("Dead & Company")); + let filtered_nodes = g.nodes().filter(filter_expr).unwrap(); + + // filter_nodes doesn't filter the iterator, it only filters the view of the nodes which includes history, edges, etc. + assert_eq!( + filtered_nodes.name().collect::>(), + vec!["Jimi", "John", "David"] + ); + + // TODO: Bug! History isn't getting filtered + let res = filtered_nodes + .iter() + .map(|n| n.history()) + .collect::>(); + assert_eq!(res, vec![vec![], vec![1], vec![]]); + + // TODO: Bug! Properties aren't getting filtered + let res = filtered_nodes + .iter() + .map(|n| n.properties().get("band")) + .collect::>(); + assert_eq!(res, vec![None, Some(Prop::str("Dead & Company")), None]); + + g.add_edge(3, "John", "Jimi", NO_PROPS, None).unwrap(); + + let res = filtered_nodes + .iter() + .map(|n| n.out_neighbours().name().collect_vec()) + .collect::>(); + assert_eq!(res, vec![Vec::::new(), vec![], vec![]]); +} + +#[test] +fn test_node_property_filter_on_nodes() { + let g = Graph::new(); + g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); + g.add_node(0, 2, [("test", 2i64)], None, None).unwrap(); + g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); + g.add_node(1, 4, [("test", 4i64)], None, None).unwrap(); + + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); + g.add_edge(2, 3, 4, NO_PROPS, None).unwrap(); + g.add_edge(3, 4, 1, NO_PROPS, None).unwrap(); + + let n1 = g.node(1).unwrap(); + + assert_eq!( + n1.filter(NodeFilter.property("test").eq(1i64)) + .unwrap() + .edges() + .id() + .collect_vec(), + vec![] + ); + assert_eq!( + n1.filter(NodeFilter.property("test").eq(2i64)) + .unwrap() + .out_neighbours() + .id() + .collect_vec(), + vec![GID::U64(2)] + ); + + let n2 = g.node(2).unwrap(); + + assert_eq!( + n2.filter(NodeFilter.property("test").gt(1i64)) + .unwrap() + .neighbours() + .id() + .collect_vec(), + vec![GID::U64(3)] + ); + + assert_eq!( + n2.filter(NodeFilter.property("test").gt(0i64)) + .unwrap() + .neighbours() + .id() + .collect_vec(), + vec![GID::U64(1), GID::U64(3)] + ); + + let gp = g.persistent_graph(); + let n1p = gp.node(1).unwrap(); + + assert_eq!( + n1p.filter(NodeFilter.property("test").eq(1i64)) + .unwrap() + .edges() + .id() + .collect_vec(), + vec![] + ); + assert_eq!( + n1p.filter(NodeFilter.property("test").eq(2i64)) + .unwrap() + .out_neighbours() + .id() + .collect_vec(), + vec![GID::U64(2)] + ); + + let n2p = gp.node(2).unwrap(); + + assert_eq!( + n2p.filter(NodeFilter.property("test").gt(1i64)) + .unwrap() + .neighbours() + .id() + .collect_vec(), + vec![GID::U64(3)] + ); + + assert_eq!( + n2p.filter(NodeFilter.property("test").gt(0i64)) + .unwrap() + .neighbours() + .id() + .collect_vec(), + vec![GID::U64(1), GID::U64(3)] + ); +} + +#[test] +fn test_node_property_filter_path() { + let g = Graph::new(); + g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); + g.add_node(1, 2, [("test", 2i64)], None, None).unwrap(); + g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); + g.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); + + let filtered_nodes = g + .nodes() + .select(NodeFilter.property("test").gt(1i64)) + .unwrap(); + assert_eq!( + filtered_nodes + .out_neighbours() + .id() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, i)| i.sorted().collect_vec()) + .collect_vec(), + vec![vec![GID::U64(1), GID::U64(3)], vec![]] + ); + + assert_eq!( + filtered_nodes + .out_neighbours() + .degree() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, i)| i.collect_vec()) + .collect_vec(), + vec![vec![2, 2], vec![]] + ); + + let filtered_nodes_p = g + .persistent_graph() + .nodes() + .select(NodeFilter.property("test").gt(1i64)) + .unwrap(); + assert_eq!( + filtered_nodes_p + .out_neighbours() + .id() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, i)| i.sorted().collect_vec()) + .collect_vec(), + vec![vec![GID::U64(1), GID::U64(3)], vec![]] + ); +} + +#[test] +fn test_node_property_filter_on_graph() { + let g = Graph::new(); + g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); + g.add_node(1, 2, [("test", 2i64)], None, None).unwrap(); + g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); + g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); + g.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); + + let gf = g.filter(NodeFilter.property("test").eq(1i64)).unwrap(); + assert_eq!(gf.edges().id().collect_vec(), vec![]); + + let gf = g.filter(NodeFilter.property("test").gt(1i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + + let gf = g.filter(NodeFilter.property("test").lt(3i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(1), GID::U64(2)), (GID::U64(2), GID::U64(1))] + ); + + let gp = g.persistent_graph(); + let gf = gp.filter(NodeFilter.property("test").eq(1i64)).unwrap(); + assert_eq!(gf.edges().id().collect_vec(), vec![]); + + let gf = gp.filter(NodeFilter.property("test").gt(1i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + + let gf = gp.filter(NodeFilter.property("test").lt(3i64)).unwrap(); + assert_eq!( + gf.edges().id().collect_vec(), + vec![(GID::U64(1), GID::U64(2)), (GID::U64(2), GID::U64(1))] + ); +} + +#[test] +fn test_filter_gt() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").gt(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv > v).is_some() + }); + + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) +} + +#[test] +fn test_filter_ge() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").ge(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv >= v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) +} + +#[test] +fn test_filter_lt() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").lt(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv < v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) +} + +#[test] +fn test_filter_le() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").le(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv <= v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) +} + +#[test] +fn test_filter_eq() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").eq(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv == v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) +} + +#[test] +fn test_filter_ne() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").ne(v); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.filter(|&vv| *vv != v ).is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) +} + +#[test] +fn test_filter_is_some() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100), + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").is_some(); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.is_some() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) +} + +#[test] +fn test_filter_is_none() { + proptest!(|( + edges in build_edge_list(100, 100), nodes in build_node_props(100) + )| { + let g = build_graph_from_edge_list(&edges); + add_node_props(&g, &nodes); + let filter = NodeFilter.property("int_prop").is_none(); + let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { + int_v.is_none() + }); + assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.edges()); + }); + assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { + assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); + // FIXME: history filtering not working properly + // assert_graph_equal(&filtered, &expected_g); + }); + }) +} + +#[test] +fn test_filter_is_none_simple_graph() { + let graph = Graph::new(); + graph + .add_node(1, 1, [("p1", 1), ("p2", 2)], Some("fire_nation"), None) + .unwrap(); + graph + .add_node(2, 1, [("p6", 6)], Some("fire_nation"), None) + .unwrap(); + graph + .add_node(2, 2, [("p4", 5)], Some("fire_nation"), None) + .unwrap(); + graph + .add_node(3, 3, [("p2", 4), ("p3", 3)], Some("water_tribe"), None) + .unwrap(); + + assert_eq!(graph.count_nodes(), 3); + + let filtered = graph.filter(NodeFilter.property("p2").is_none()).unwrap(); + let ids = filtered.nodes().name().collect_vec(); + + assert_eq!(ids, vec!["2"]); +} diff --git a/raphtory-test-utils/tests/node_test.rs b/raphtory-test-utils/tests/node_test.rs new file mode 100644 index 0000000000..1dccdeccaa --- /dev/null +++ b/raphtory-test-utils/tests/node_test.rs @@ -0,0 +1,433 @@ +use raphtory::{ + core::storage::timeindex::AsTime, + db::{ + api::view::Filter, + graph::views::filter::model::{NodeViewFilterOps, ViewWrapOps}, + }, + prelude::*, +}; +use raphtory_api::core::storage::arc_str::ArcStr; +use raphtory_test_utils::{test_storage, test_utils::test_graph}; +use std::collections::HashMap; + +#[test] +fn test_earliest_time() { + let graph = Graph::new(); + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); + + // FIXME: Node add without properties not showing up (Issue #46) + test_graph(&graph, |graph| { + let view = graph.before(2); + assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 0); + assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 1); + + let view = graph.before(3); + assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 0); + assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 2); + + let view = graph.after(0); + assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 1); + assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 2); + + let view = graph.after(2); + assert_eq!(view.node(1), None); + assert_eq!(view.node(1), None); + + let view = graph.at(1); + assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 1); + assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 1); + }); +} + +#[test] +fn test_properties() { + let graph = Graph::new(); + let props = [("test", "test")]; + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 1, props, None, None).unwrap(); + + // FIXME: Node add without properties not showing up (Issue #46) + test_graph(&graph, |graph| { + let v1 = graph.node(1).unwrap(); + let v1_w = graph.window(0, 1).node(1).unwrap(); + assert_eq!( + v1.properties().as_map(), + [(ArcStr::from("test"), Prop::str("test"))] + .into_iter() + .collect::>() + ); + assert_eq!(v1_w.properties().as_map(), HashMap::default()) + }); +} + +#[test] +fn test_property_additions() { + let graph = Graph::new(); + let props = [("test", "test")]; + let v1 = graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + v1.add_updates(2, props, None).unwrap(); + let v1_w = v1.window(0, 1); + assert_eq!( + v1.properties().as_map(), + props + .into_iter() + .map(|(k, v)| (ArcStr::from(k), v.into_prop())) + .collect::>() + ); + assert_eq!(v1_w.properties().as_map(), HashMap::default()) +} + +#[test] +fn test_metadata_additions() { + let g = Graph::new(); + let v1 = g.add_node(0, 1, NO_PROPS, None, None).unwrap(); + v1.add_metadata([("test", "test")]).unwrap(); + assert_eq!(v1.metadata().get("test"), Some("test".into())) +} + +#[test] +fn test_metadata_updates() { + let g = Graph::new(); + let v1 = g.add_node(0, 1, NO_PROPS, None, None).unwrap(); + v1.add_metadata([("test", "test")]).unwrap(); + v1.update_metadata([("test", "test2")]).unwrap(); + assert_eq!(v1.metadata().get("test"), Some("test2".into())) +} + +#[test] +#[ignore] // likely we don't want to handle it globally like this anymore, maybe we should introduce an explicit categorical property type? +fn test_string_deduplication() { + let g = Graph::new(); + let v1 = g + .add_node(0, 1, [("test1", "test"), ("test2", "test")], None, None) + .unwrap(); + let s1 = v1.properties().get("test1").unwrap_str(); + let s2 = v1.properties().get("test2").unwrap_str(); + + assert_eq!(s1.as_ptr(), s2.as_ptr()) +} + +#[test] +fn test_edge_history_and_timestamps() { + let graph = Graph::new(); + + // Add nodes + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + + // Add edges at different times + graph.add_edge(10, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(20, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(30, 2, 1, NO_PROPS, None).unwrap(); + graph.add_edge(5, 1, 3, NO_PROPS, None).unwrap(); // Earlier edge to same pair + + test_storage!(&graph, |graph| { + let node1 = graph.node(1).unwrap(); + + // Test edge_history (chronological order) + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![5, 10, 20, 30]); + + // Test edge_history_rev (reverse chronological order) + let history_rev: Vec<_> = node1.edge_history_rev().map(|(t, _)| t.t()).collect(); + assert_eq!(history_rev, vec![30, 20, 10, 5]); + + // Test earliest_edge_time + assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); + + // Test latest_edge_time + assert_eq!(node1.latest_edge_time().unwrap().t(), 30); + }); +} + +#[test] +fn test_edge_timestamps_with_windows() { + let graph = Graph::new(); + + // Add nodes + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + + // Add edges at different times + graph.add_edge(5, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(15, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(25, 2, 1, NO_PROPS, None).unwrap(); + graph.add_edge(35, 1, 3, NO_PROPS, None).unwrap(); + + test_graph(&graph, |graph| { + // Test window 0-20 + let windowed = graph.window(0, 20); + let node1 = windowed.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![5, 15]); + + assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); + assert_eq!(node1.latest_edge_time().unwrap().t(), 15); + + // Test window 10-30 + let windowed = graph.window(10, 30); + let node1 = windowed.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![15, 25]); + + assert_eq!(node1.earliest_edge_time().unwrap().t(), 15); + assert_eq!(node1.latest_edge_time().unwrap().t(), 25); + + // Test window after all edges + let windowed = graph.after(40); + assert_eq!(windowed.node(1), None); // Node has no edges in this window + }); +} + +#[test] +fn test_edge_timestamps_with_layers() { + let graph = Graph::new(); + + // Add nodes + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + + // Add edges on different layers + graph.add_edge(10, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(20, 1, 3, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(30, 2, 1, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(5, 1, 3, NO_PROPS, Some("layer2")).unwrap(); + + // Test all layers + let node1 = graph.node(1).unwrap(); + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![5, 10, 20, 30]); + assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); + assert_eq!(node1.latest_edge_time().unwrap().t(), 30); + + // Test layer1 only + let layer1_graph = graph.layers(vec!["layer1"]).unwrap(); + let node1_layer1 = layer1_graph.node(1).unwrap(); + let history: Vec<_> = node1_layer1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![10, 30]); + assert_eq!(node1_layer1.earliest_edge_time().unwrap().t(), 10); + assert_eq!(node1_layer1.latest_edge_time().unwrap().t(), 30); + + // Test layer2 only + let layer2_graph = graph.layers(vec!["layer2"]).unwrap(); + let node1_layer2 = layer2_graph.node(1).unwrap(); + let history: Vec<_> = node1_layer2.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![5, 20]); + assert_eq!(node1_layer2.earliest_edge_time().unwrap().t(), 5); + assert_eq!(node1_layer2.latest_edge_time().unwrap().t(), 20); +} + +#[test] +fn test_edge_timestamps_overlapping_windows_and_layers() { + let graph = Graph::new(); + + // Add nodes + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 4, NO_PROPS, None, None).unwrap(); + + // Add edges with overlapping time ranges across multiple layers + graph.add_edge(5, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(10, 1, 3, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(15, 1, 4, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(20, 2, 1, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(25, 3, 1, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(30, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(35, 1, 4, NO_PROPS, Some("layer2")).unwrap(); + + test_graph(&graph, |graph| { + // Test overlapping window (8-22) with layer1 + let windowed_layer1 = graph.window(8, 22).layers(vec!["layer1"]).unwrap(); + let node1 = windowed_layer1.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![10, 20]); + assert_eq!(node1.earliest_edge_time().unwrap().t(), 10); + assert_eq!(node1.latest_edge_time().unwrap().t(), 20); + + // Test overlapping window (12-28) with layer2 + let windowed_layer2 = graph.window(12, 28).layers(vec!["layer2"]).unwrap(); + let node1 = windowed_layer2.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![15, 25]); + assert_eq!(node1.earliest_edge_time().unwrap().t(), 15); + assert_eq!(node1.latest_edge_time().unwrap().t(), 25); + + // Test overlapping window (18-32) with both layers + let windowed_both = graph + .window(18, 32) + .layers(vec!["layer1", "layer2"]) + .unwrap(); + let node1 = windowed_both.node(1).unwrap(); + + let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); + assert_eq!(history, vec![20, 25, 30]); + assert_eq!(node1.earliest_edge_time().unwrap().t(), 20); + assert_eq!(node1.latest_edge_time().unwrap().t(), 30); + + // Test edge case: window with no edges for the node + let empty_window = graph.window(50, 60); + assert_eq!(empty_window.node(1), None); + }); +} + +#[test] +fn test_edge_timestamps_no_edges() { + let graph = Graph::new(); + + // Add a node but no edges + graph.add_node(10, 1, NO_PROPS, None, None).unwrap(); + + test_graph(&graph, |graph| { + let node1 = graph.node(1).unwrap(); + + // Node exists but has no edges + assert_eq!(node1.edge_history().count(), 0); + assert_eq!(node1.edge_history_rev().count(), 0); + assert_eq!(node1.earliest_edge_time(), None); + assert_eq!(node1.latest_edge_time(), None); + }); +} + +#[test] +fn test_node_layers() { + let graph = Graph::new(); + graph + .add_node(0, 1, [("a", "test")], None, Some("fire_nation")) + .unwrap(); + graph + .add_node(0, 2, NO_PROPS, None, Some("fire_nation")) + .unwrap(); + graph + .add_node(0, 3, [("b", 3)], None, Some("air_nomads")) + .unwrap(); + + let filter_expr = NodeFilter.layer("fire_nation").is_active(); + let ids = graph + .filter(filter_expr) + .unwrap() + .nodes() + .iter() + .map(|n| n.name()) + .collect::>(); + + assert_eq!(ids, vec!["1", "2"]); +} + +/// Nodes added without a layer (`layer=None`) go into STATIC_GRAPH_LAYER_ID and must be +/// visible as active in every layer-restricted view. +#[test] +fn test_unlayered_node_visible_in_all_layer_views() { + let graph = Graph::new(); + // Node 1 added without a layer — should appear in any layer view + graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); + // Node 2 added with "fire_nation" — should only appear in that view + graph + .add_node(0, 2, NO_PROPS, None, Some("fire_nation")) + .unwrap(); + + let fire_view = graph + .filter(NodeFilter.layer("fire_nation").is_active()) + .unwrap(); + let mut ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); + ids.sort(); + // Both node 1 (unlayered/static) and node 2 (fire_nation) should be visible + assert_eq!( + ids, + vec!["1", "2"], + "unlayered node must appear in fire_nation view" + ); +} + +/// A node added with layer A must NOT appear in a filter restricted to layer B. +#[test] +fn test_layered_node_not_visible_in_other_layer_view() { + let graph = Graph::new(); + graph + .add_node(0, 1, NO_PROPS, None, Some("fire_nation")) + .unwrap(); + graph + .add_node(0, 2, NO_PROPS, None, Some("air_nomads")) + .unwrap(); + + let fire_view = graph + .filter(NodeFilter.layer("fire_nation").is_active()) + .unwrap(); + let ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); + assert_eq!( + ids, + vec!["1"], + "air_nomads node must not appear in fire_nation view" + ); +} + +/// Nodes added without a layer should appear in a windowed + layer-filtered view +/// as long as they have activity within the window. +#[test] +fn test_unlayered_node_visible_in_windowed_layer_view() { + let graph = Graph::new(); + graph.add_node(5, 1, NO_PROPS, None, None).unwrap(); // unlayered, t=5 + graph + .add_node(5, 2, NO_PROPS, None, Some("fire_nation")) + .unwrap(); // layered, t=5 + graph + .add_node(5, 3, NO_PROPS, None, Some("air_nomads")) + .unwrap(); + + // Window [0, 10) covers t=5; filter to fire_nation + let view = graph.window(0, 10); + let fire_view = view + .filter(NodeFilter.layer("fire_nation").is_active()) + .unwrap(); + let mut ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); + ids.sort(); + assert_eq!( + ids, + vec!["1", "2"], + "unlayered node at t=5 must appear in windowed fire_nation view" + ); + + // Window [10, 20) does NOT cover t=5, so no nodes should be active + let view_out = graph.window(10, 20); + let fire_view_out = view_out + .filter(NodeFilter.layer("fire_nation").is_active()) + .unwrap(); + assert_eq!( + fire_view_out.count_nodes(), + 0, + "no nodes should be active outside the window" + ); +} + +/// Node history (timestamps) should be scoped to the requested layer, not bleed +/// across layers. +#[test] +fn test_node_history_scoped_to_layer() { + let graph = Graph::new(); + // Node 1 exists in fire_nation at t=1 and air_nomads at t=2 + graph + .add_node(1, 1, NO_PROPS, None, Some("fire_nation")) + .unwrap(); + graph + .add_node(2, 1, NO_PROPS, None, Some("air_nomads")) + .unwrap(); + + let fire_layer = graph.layers("fire_nation").unwrap(); + let node = fire_layer.node(1).unwrap(); + let history: Vec = node.history().t().collect(); + // Only the fire_nation timestamp should be visible + assert_eq!( + history, + vec![1], + "history must not include timestamps from other layers" + ); +} diff --git a/raphtory/tests/proto_test.rs b/raphtory-test-utils/tests/proto_test.rs similarity index 100% rename from raphtory/tests/proto_test.rs rename to raphtory-test-utils/tests/proto_test.rs diff --git a/raphtory/tests/serialise_test.rs b/raphtory-test-utils/tests/serialise_test.rs similarity index 100% rename from raphtory/tests/serialise_test.rs rename to raphtory-test-utils/tests/serialise_test.rs diff --git a/raphtory-test-utils/tests/subgraph_tests.rs b/raphtory-test-utils/tests/subgraph_tests.rs new file mode 100644 index 0000000000..85ac56011c --- /dev/null +++ b/raphtory-test-utils/tests/subgraph_tests.rs @@ -0,0 +1,559 @@ +use ahash::HashSet; +use itertools::Itertools; +use proptest::{proptest, sample::subsequence}; +use raphtory::{ + algorithms::{components::weakly_connected_components, motifs::triangle_count::triangle_count}, + db::graph::{graph::assert_graph_equal, views::deletion_graph::PersistentGraph}, + prelude::*, +}; +use raphtory_storage::mutation::addition_ops::InternalAdditionOps; +use raphtory_test_utils::{ + test_storage, + test_utils::{build_graph, build_graph_strat}, +}; +use serde_json::json; +use std::collections::BTreeSet; + +#[test] +fn test_materialize_no_edges() { + let graph = Graph::new(); + + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 2, NO_PROPS, None, None).unwrap(); + + test_storage!(&graph, |graph| { + let sg = graph.subgraph([1, 2, 1]); // <- duplicated nodes should have no effect + + let actual = sg.materialize().unwrap().into_events().unwrap(); + assert_graph_equal(&actual, &sg); + }); +} + +#[test] +fn test_remove_degree1_triangle_count() { + let graph = Graph::new(); + let edges = vec![ + (1, 2, 1), + (1, 3, 2), + (1, 4, 3), + (3, 1, 4), + (3, 4, 5), + (3, 5, 6), + (4, 5, 7), + (5, 6, 8), + (5, 8, 9), + (7, 5, 10), + (8, 5, 11), + (1, 9, 12), + (9, 1, 13), + (6, 3, 14), + (4, 8, 15), + (8, 3, 16), + (5, 10, 17), + (10, 5, 18), + (10, 8, 19), + (1, 11, 20), + (11, 1, 21), + (9, 11, 22), + (11, 9, 23), + ]; + for (src, dst, ts) in edges { + graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let subgraph = graph.subgraph(graph.nodes().into_iter().filter(|v| v.degree() > 1)); + let ts = triangle_count(&subgraph, None); + let tg = triangle_count(graph, None); + assert_eq!(ts, tg) + }); +} + +#[test] +fn layer_materialize() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(0, 3, 4, NO_PROPS, Some("2")).unwrap(); + + test_storage!(&graph, |graph| { + let sg = graph.subgraph([1, 2]); + let sgm = sg.materialize().unwrap(); + assert_eq!( + sg.unique_layers().collect_vec(), + sgm.unique_layers().collect_vec() + ); + }); +} + +#[test] +fn test_cc() { + let graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); + graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(1, 3, 4, NO_PROPS, Some("1")).unwrap(); + let sg = graph.subgraph([0, 1, 3, 4]); + let cc = weakly_connected_components(&sg); + let groups = cc.groups(); + let group_sets = groups + .iter() + .map(|(_, g)| { + g.iter() + .map(|node| node.id()) + .sorted() + .collect::>() + }) + .collect::>(); + assert_eq!( + group_sets, + HashSet::from_iter([ + BTreeSet::from([GID::U64(0), GID::U64(1)]), + BTreeSet::from([GID::U64(3), GID::U64(4)]) + ]) + ); +} + +#[test] +fn test_layer_edges() { + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(1, 0, 1, NO_PROPS, Some("2")).unwrap(); + + assert_eq!( + graph.subgraph([0, 1]).edges().id().collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); + assert_eq!( + graph + .subgraph([0, 1]) + .valid_layers("1") + .edges() + .id() + .collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); +} + +pub mod test_filters_node_subgraph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::{node_subgraph::NodeSubgraph, window_graph::WindowedGraph}, + }, + prelude::{GraphViewOps, NodeViewOps, TimeOps}, + }; + use raphtory_test_utils::assertions::GraphTransformer; + use std::ops::Range; + + struct NodeSubgraphTransformer(Option>); + + impl GraphTransformer for NodeSubgraphTransformer { + type Return = NodeSubgraph; + fn apply(&self, graph: G) -> Self::Return { + let node_names: Vec = self + .0 + .clone() + .unwrap_or_else(|| graph.nodes().name().collect::>()); + graph.subgraph(node_names) + } + } + + struct WindowedNodeSubgraphTransformer(Option>, Range); + + impl GraphTransformer for WindowedNodeSubgraphTransformer { + type Return = NodeSubgraph>; + fn apply(&self, graph: G) -> Self::Return { + let graph = graph.window(self.1.start, self.1.end); + let node_names: Vec = self + .0 + .clone() + .unwrap_or_else(|| graph.nodes().name().collect::>()); + graph.subgraph(node_names) + } + } + + mod test_nodes_filters_node_subgraph { + use crate::test_filters_node_subgraph::{ + NodeSubgraphTransformer, WindowedNodeSubgraphTransformer, + }; + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }, + }, + prelude::{AdditionOps, NodeFilter}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_test_utils::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, + TestVariants, + }; + + fn init_graph(graph: G) -> G { + let nodes = vec![ + (6, "N1", vec![("p1", Prop::U64(2u64))]), + (7, "N1", vec![("p1", Prop::U64(1u64))]), + (6, "N2", vec![("p1", Prop::U64(1u64))]), + (7, "N2", vec![("p1", Prop::U64(2u64))]), + (8, "N3", vec![("p1", Prop::U64(1u64))]), + (9, "N4", vec![("p1", Prop::U64(1u64))]), + (5, "N5", vec![("p1", Prop::U64(1u64))]), + (6, "N5", vec![("p1", Prop::U64(2u64))]), + (5, "N6", vec![("p1", Prop::U64(1u64))]), + (6, "N6", vec![("p1", Prop::U64(1u64))]), + (3, "N7", vec![("p1", Prop::U64(1u64))]), + (5, "N7", vec![("p1", Prop::U64(1u64))]), + (3, "N8", vec![("p1", Prop::U64(1u64))]), + (4, "N8", vec![("p1", Prop::U64(2u64))]), + ]; + + for (id, name, props) in &nodes { + graph + .add_node(*id, name, props.clone(), None, None) + .unwrap(); + } + + graph + } + + #[test] + fn test_search_nodes_subgraph() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = ["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + NodeSubgraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + NodeSubgraphTransformer(None), + filter, + &expected_results, + TestVariants::All, + ); + + let node_names: Option> = + Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N3", "N4"]; + assert_filter_nodes_results( + init_graph, + NodeSubgraphTransformer(node_names.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + NodeSubgraphTransformer(node_names), + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_search_nodes_subgraph_w() { + // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let node_names: Option> = Some(vec!["N3".into()]); + let filter = NodeFilter.property("p1").gt(0u64); + let expected_results = vec!["N3"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_search_nodes_pg_w() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let node_names: Option> = + Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); + let filter = NodeFilter.property("p1").ge(1u64); + let expected_results = vec!["N2", "N3", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + } + + mod test_edges_filters_node_subgraph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }, + }, + prelude::{AdditionOps, EdgeFilter}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_test_utils::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestVariants, + }; + + use crate::test_filters_node_subgraph::{ + NodeSubgraphTransformer, WindowedNodeSubgraphTransformer, + }; + + fn init_graph(graph: G) -> G { + let edges = vec![ + (6, "N1", "N2", vec![("p1", Prop::U64(2u64))]), + (7, "N1", "N2", vec![("p1", Prop::U64(1u64))]), + (6, "N2", "N3", vec![("p1", Prop::U64(1u64))]), + (7, "N2", "N3", vec![("p1", Prop::U64(2u64))]), + (8, "N3", "N4", vec![("p1", Prop::U64(1u64))]), + (9, "N4", "N5", vec![("p1", Prop::U64(1u64))]), + (5, "N5", "N6", vec![("p1", Prop::U64(1u64))]), + (6, "N5", "N6", vec![("p1", Prop::U64(2u64))]), + (5, "N6", "N7", vec![("p1", Prop::U64(1u64))]), + (6, "N6", "N7", vec![("p1", Prop::U64(1u64))]), + (3, "N7", "N8", vec![("p1", Prop::U64(1u64))]), + (5, "N7", "N8", vec![("p1", Prop::U64(1u64))]), + (3, "N8", "N1", vec![("p1", Prop::U64(1u64))]), + (4, "N8", "N1", vec![("p1", Prop::U64(2u64))]), + ]; + + for (id, src, tgt, props) in &edges { + graph.add_edge(*id, src, tgt, props.clone(), None).unwrap(); + } + + graph + } + + #[test] + fn test_edges_filters() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + NodeSubgraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + NodeSubgraphTransformer(None), + filter, + &expected_results, + TestVariants::All, + ); + + let node_names: Option> = + Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = vec!["N3->N4", "N4->N5"]; + assert_filter_edges_results( + init_graph, + NodeSubgraphTransformer(node_names.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + NodeSubgraphTransformer(node_names), + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_filters_w() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let node_names: Option> = + Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); + let filter = EdgeFilter.property("p1").ge(1u64); + let expected_results = vec!["N2->N3", "N3->N4"]; + assert_filter_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_w() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let node_names: Option> = Some(vec![ + "N2".into(), + "N3".into(), + "N4".into(), + "N5".into(), + "N6".into(), + ]); + let filter = EdgeFilter.property("p1").lt(2u64); + let expected_results = vec!["N3->N4"]; + assert_filter_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowedNodeSubgraphTransformer(node_names, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + } +} + +#[test] +fn nodes_without_updates_are_filtered() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + let expected = Graph::new(); + expected.resolve_layer(None).unwrap(); + let subgraph = g.subgraph([0]); + assert_graph_equal(&subgraph, &expected); +} + +#[test] +fn materialize_proptest() { + proptest!(|(graph in build_graph_strat(10, 10, 10, 10, false), nodes in subsequence((0..10).collect::>(), 0..10))| { + let graph = Graph::from(build_graph(&graph)); + let subgraph = graph.subgraph(nodes); + assert_graph_equal(&subgraph, &subgraph.materialize().unwrap()); + }) +} + +#[test] +fn materialize_proptest_failure() { + let graph_f = serde_json::from_value(json!({"nodes":{},"edges":[[[1,1,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let graph = Graph::from(build_graph(&graph_f)); + let subgraph = graph.subgraph([1]); + let nodes = subgraph.default_layer().nodes().id().collect_vec(); + dbg!(nodes); + assert_eq!(subgraph.default_layer().count_nodes(), 0); + assert_eq!(subgraph.count_edges(), 1); + let materialised = subgraph.materialize().unwrap(); + assert_graph_equal(&subgraph, &materialised); +} + +#[test] +fn materialize_persistent_proptest() { + proptest!(|(graph in build_graph_strat(10, 10, 10, 10, true), nodes in subsequence((0..10).collect::>(), 0..10))| { + let graph = PersistentGraph::from(build_graph(&graph)); + let subgraph = graph.subgraph(nodes); + assert_graph_equal(&subgraph, &subgraph.materialize().unwrap()); + }) +} + +#[test] +fn test_subgraph_only_deletion() { + let g = PersistentGraph::new(); + g.delete_edge(0, 0, 1, None).unwrap(); + let sg = g.subgraph([0]); + let expected = PersistentGraph::new(); + expected.resolve_layer(None).unwrap(); + assert_graph_equal(&sg, &expected); +} diff --git a/raphtory-test-utils/tests/test_deletions.rs b/raphtory-test-utils/tests/test_deletions.rs new file mode 100644 index 0000000000..f150de496a --- /dev/null +++ b/raphtory-test-utils/tests/test_deletions.rs @@ -0,0 +1,1184 @@ +use itertools::Itertools; +use proptest::{arbitrary::any, proptest, sample::subsequence}; +use raphtory::{ + db::graph::{ + edge::EdgeView, + graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, + views::deletion_graph::PersistentGraph, + }, + prelude::*, +}; +use raphtory_api::core::{ + entities::GID, + storage::timeindex::{AsTime, EventTime}, +}; +use raphtory_storage::mutation::addition_ops::InternalAdditionOps; +use raphtory_test_utils::{ + test_storage, + test_utils::{build_graph, build_graph_strat}, +}; +use rayon::ThreadPoolBuilder; +use std::ops::Range; + +#[test] +fn test_nodes() { + let g = PersistentGraph::new(); + + let edges = [ + (0, 1, 2, "assigned"), + (1, 1, 3, "assigned"), + (2, 4, 2, "has"), + (3, 4, 2, "has"), + (4, 5, 2, "blocks"), + (5, 4, 5, "has"), + (6, 6, 5, "assigned"), + ]; + + for (time, src, dst, layer) in edges { + g.add_edge(time, src, dst, [("added", Prop::I64(0))], Some(layer)) + .unwrap(); + } + + let nodes = g + .window(0, 1701786285758) + .layers(vec!["assigned", "has", "blocks"]) + .unwrap() + .nodes() + .into_iter() + .map(|vv| vv.name()) + .collect_vec(); + + assert_eq!(nodes, vec!["1", "2", "3", "4", "5", "6"]); + + let nodes = g + .at(1701786285758) + .layers(vec!["assigned", "has", "blocks"]) + .unwrap() + .nodes() + .into_iter() + .map(|vv| vv.name()) + .collect_vec(); + + assert_eq!(nodes, vec!["1", "2", "3", "4", "5", "6"]); +} + +#[test] +fn test_edge_deletions() { + let g = PersistentGraph::new(); + + g.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) + .unwrap(); + g.delete_edge(10, 0, 1, None).unwrap(); + + assert_eq!( + g.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + + assert_eq!( + g.window(1, 2).edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + + assert_eq!(g.window(1, 2).count_edges(), 1); + + assert_eq!(g.window(11, 12).count_edges(), 0); + + assert_eq!( + g.window(1, 2) + .edge(0, 1) + .unwrap() + .properties() + .get("added") + .unwrap_i64(), + 0 + ); + + assert!(g.window(11, 12).edge(0, 1).is_none()); + + assert_eq!( + g.window(1, 2) + .edge(0, 1) + .unwrap() + .properties() + .temporal() + .get("added") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + vec![(1, Prop::I64(0))] + ); + + assert_eq!(g.window(1, 2).node(0).unwrap().out_degree(), 1) +} + +#[test] +fn test_window_semantics() { + let g = PersistentGraph::new(); + g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); + g.delete_edge(10, 1, 2, None).unwrap(); + + assert_eq!(g.count_edges(), 1); + + assert_eq!(g.at(12).count_edges(), 0); + assert_eq!(g.at(11).count_edges(), 0); + assert_eq!(g.at(10).count_edges(), 0); + assert_eq!(g.at(9).count_edges(), 1); + assert_eq!(g.window(5, 9).count_edges(), 1); + assert_eq!(g.window(5, 10).count_edges(), 1); + assert_eq!(g.window(5, 11).count_edges(), 1); + assert_eq!(g.window(10, 12).count_edges(), 0); + assert_eq!(g.before(10).count_edges(), 1); + assert_eq!(g.after(10).count_edges(), 0); +} + +#[test] +fn test_timestamps() { + let g = PersistentGraph::new(); + let e = g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); + assert_eq!(e.earliest_time().unwrap(), 1); // time of first addition + assert_eq!(e.latest_time().map(|t| t.t()), Some(1)); // not deleted so alive forever + g.delete_edge(10, 1, 2, None).unwrap(); + assert_eq!(e.latest_time().unwrap(), 10); // deleted, so time of last deletion + + g.delete_edge(10, 3, 4, None).unwrap(); + let e = g.edge(3, 4).unwrap(); + assert_eq!(e.earliest_time().unwrap(), 10); // only deleted, earliest and latest time are the same + assert_eq!(e.latest_time().unwrap(), 10); + g.add_edge(1, 3, 4, [("test", "test")], None).unwrap(); + assert_eq!(e.latest_time().unwrap(), 10); + assert_eq!(e.earliest_time().unwrap(), 1); // added so timestamp is now the first addition +} + +#[test] +fn test_materialize_only_deletion() { + let g = PersistentGraph::new(); + g.delete_edge(1, 1, 2, None).unwrap(); + g.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); + g.delete_edge(5, 1, 2, None).unwrap(); + g.delete_edge(10, 1, 2, None).unwrap(); + assert_eq!( + g.window(0, 11).count_temporal_edges(), + g.count_temporal_edges() + ); + assert_graph_equal(&g.materialize().unwrap(), &g); +} + +#[test] +fn materialize_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { + let g = PersistentGraph::from(build_graph(&graph_f)); + let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); + }) +} + +#[test] +fn materialize_window_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = PersistentGraph::from(build_graph(&graph_f)); + let gw = g.window(w.start, w.end); + let gmw = gw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gw, &gmw); + }) +} + +#[test] +fn test_multilayer_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(10, 0, 0, NO_PROPS, Some("a")).unwrap(); + println!("all: {g:?}"); + let gw = g.window(1, 10); + println!("windowed nodes: {:?}", gw.nodes()); + let gm = gw.materialize().unwrap(); + + println!("materialized: {:?}", gm); + + assert_eq!(gw.valid_layers("a").count_nodes(), 0); + assert_eq!(gm.valid_layers("a").count_nodes(), 0); + + let expected = PersistentGraph::new(); + expected.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); + expected.resolve_layer(Some("a")).unwrap(); // empty layer exists + + println!("expected: {:?}", expected); + assert_persistent_materialize_graph_equal(&gw, &expected); + + assert_persistent_materialize_graph_equal(&gw, &gm); +} + +#[test] +fn test_multilayer_window2() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); + let gw = g.window(1, i64::MAX); + let gm = gw.materialize().unwrap(); + println!("original: {g:?}"); + assert_eq!(g.default_layer().count_nodes(), 1); + println!("materialized: {gm:?}"); + assert_eq!(gm.default_layer().count_nodes(), 1); + + assert_persistent_materialize_graph_equal(&gw, &gm.clone().into_persistent().unwrap()); + assert_persistent_materialize_graph_equal(&gw, &gm); +} + +#[test] +fn test_deletion_at_window_start() { + let g = PersistentGraph::new(); + g.add_edge(2, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(0, 0, 1, None).unwrap(); + + // deletion at start of window is not part of the view + let gw = g.window(0, 1); + assert!(gw.is_empty()); + + let gw = g.window(0, 3); + assert_eq!(gw.node(0).unwrap().earliest_time().unwrap().t(), 2); + assert_eq!(gw.node(1).unwrap().earliest_time().unwrap().t(), 2); +} + +#[test] +fn materialize_window_layers_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), l in subsequence(&["a", "b"], 0..=2), num_threads in 1..=16usize)| { + let pool = ThreadPoolBuilder::new().num_threads(num_threads).build().unwrap(); + pool.install(|| { + let g = PersistentGraph::from(build_graph(&graph_f)); + let glw = g.valid_layers(l.clone()).window(w.start, w.end); + let gmlw = glw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&glw, &gmlw); + }) + + }) +} + +#[test] +fn materialize_window_multilayer() { + let g = PersistentGraph::new(); + g.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); + g.delete_edge(3, 0, 0, Some("a")).unwrap(); + let w = 0..10; + let glw = g.valid_layers("a").window(w.start, w.end); + let gmlw = glw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&glw, &gmlw); +} + +#[test] +fn test_materialize_deleted_edge() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(1, 0, 1, None).unwrap(); + g.delete_edge(5, 0, 1, None).unwrap(); + + let gw = g.window(2, 10); + + let gm = gw.materialize().unwrap(); + + assert_graph_equal(&gw, &gm); +} + +#[test] +fn test_addition_deletion_multilayer_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); + g.delete_edge(0, 0, 0, None).unwrap(); + let gw = g.window(0, 0).valid_layers("a"); + let expected_gw = PersistentGraph::new(); + expected_gw.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gw, &expected_gw); + let gwm = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gwm); +} + +#[test] +fn materialize_broken_time() { + let g = PersistentGraph::new(); + g.add_edge( + -7868307470600541330, + 0, + 0, + [("test", Prop::map([("x", "y")]))], + Some("a"), + ) + .unwrap(); + g.add_edge( + -8675512464616562592, + 0, + 0, + [("test", Prop::map([("z", "hi")]))], + None, + ) + .unwrap(); + g.edge(0, 0) + .unwrap() + .add_metadata([("other", "b")], None) + .unwrap(); + let gw = g.window(-7549523977641994620, -995047120251067629); + assert_persistent_materialize_graph_equal(&gw, &gw.materialize().unwrap()) +} + +#[test] +fn test_materialize_window_start_before_node_add() { + let g = PersistentGraph::new(); + g.add_node(-1, 0, [("test", "test")], None, None).unwrap(); + g.add_node(5, 0, [("test", "blob")], None, None).unwrap(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + let gw = g.window(-5, 8); + let gmw = gw.materialize().unwrap(); + assert_graph_equal(&gw, &gmw); +} + +#[test] +fn test_materialize_edge_metadata() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.add_metadata([("test", "test")], None).unwrap(); + g.delete_edge(1, 1, 2, None).unwrap(); + + let gw = g.after(1); + let gmw = gw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gw, &gmw); +} + +#[test] +fn test_metadata_multiple_layers() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.add_metadata([("test", "test")], None).unwrap(); + g.delete_edge(1, 1, 2, None).unwrap(); + assert_eq!( + g.edge(1, 2).unwrap().metadata().as_vec(), + [("test".into(), Prop::map([("_default", "test")]))] + ); + let gw = g.after(1); + assert!(gw + .edge(1, 2) + .unwrap() + .metadata() + .iter_filtered() + .next() + .is_none()); + let g_before = g.before(1); + assert_eq!( + g_before.edge(1, 2).unwrap().metadata().as_vec(), + [("test".into(), Prop::map([("_default", "test")]))] + ); +} + +#[test] +fn test_materialize_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.delete_edge(10, 1, 2, None).unwrap(); + + let gm = g + .window(3, 5) + .materialize() + .unwrap() + .into_persistent() + .unwrap(); + assert_persistent_materialize_graph_equal(&g.window(3, 5), &gm); // ignore start of window as it has different updates by design +} + +#[test] +fn test_materialize_window_node_props() { + let g = Graph::new(); + g.add_node(0, 1, [("test", "test")], None, None).unwrap(); + + test_storage!(&g, |g| { + let g = g.persistent_graph(); + + let wg = g.window(3, 5); + let mg = wg.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&wg, &mg); + }); +} + +#[test] +fn test_exploded_latest_time() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.delete_edge(10, 1, 2, None).unwrap(); + assert_eq!(e.latest_time().unwrap().t(), 10); + assert_eq!( + e.explode() + .latest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + vec![Some(10)] + ); +} + +#[test] +fn test_exploded_window() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + for t in [5, 10, 15] { + e.add_updates(t, NO_PROPS, None).unwrap(); + } + assert_eq!( + e.after(2).explode().time().flatten().collect_vec(), + [3, 5, 10, 15] + ); +} + +#[test] +fn test_edge_properties() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, [("test", "test")], None).unwrap(); + assert_eq!(e.properties().get("test").unwrap_str(), "test"); + e.delete(10, None).unwrap(); + assert_eq!(e.properties().get("test").unwrap_str(), "test"); + assert_eq!(e.at(10).properties().get("test"), None); + e.add_updates(11, [("test", "test11")], None).unwrap(); + assert_eq!( + e.window(10, 12).properties().get("test").unwrap_str(), + "test11" + ); + assert_eq!( + e.window(5, 12) + .properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + vec![(5, Prop::str("test")), (11i64, Prop::str("test11"))], + ); +} + +#[test] +fn test_multiple_edge_properties() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test1", "test1")], None).unwrap(); + g.add_edge(1, 0, 1, [("test2", "test2")], None).unwrap(); + + let e = g.edge(0, 1).unwrap(); + assert_eq!(e.properties().get("test1").unwrap_str(), "test1"); + assert_eq!(e.properties().get("test2").unwrap_str(), "test2"); + + let ew = e.window(1, 10); + assert_eq!(ew.properties().get("test1").unwrap_str(), "test1"); + assert_eq!(ew.properties().get("test2").unwrap_str(), "test2"); +} + +#[test] +fn test_edge_history() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.delete(5, None).unwrap(); + e.add_updates(10, NO_PROPS, None).unwrap(); + assert_eq!(e.history(), [0, 10]); + assert_eq!(e.after(1).history(), [10]); + assert!(e.window(1, 4).history().is_empty()); + + // exploded edge still exists + assert_eq!( + e.window(1, 4) + .explode() + .earliest_time() + .flatten() + .collect_vec(), + [1] + ); +} + +#[test] +fn test_ordering_of_addition_and_deletion() { + let g = PersistentGraph::new(); + + //deletion before addition (deletion has no effect and edge exists (1, inf) + g.delete_edge(1, 1, 2, None).unwrap(); + let e_1_2 = g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); + + //deletion after addition (edge exists only at 2) + let e_3_4 = g.add_edge(2, 3, 4, [("test", "test")], None).unwrap(); + g.delete_edge(2, 3, 4, None).unwrap(); + + assert_eq!(e_1_2.at(0).properties().get("test"), None); + assert_eq!(e_1_2.at(1).properties().get("test").unwrap_str(), "test"); + assert_eq!(e_1_2.at(2).properties().get("test").unwrap_str(), "test"); + + assert_eq!(e_3_4.at(0).properties().get("test"), None); + assert_eq!(e_3_4.at(2).properties().get("test"), None); + assert_eq!(e_3_4.at(3).properties().get("test"), None); + + assert!(!g.window(0, 1).has_edge(1, 2)); + assert!(!g.window(0, 2).has_edge(3, 4)); + assert!(g.window(1, 2).has_edge(1, 2)); + assert!(!g.window(2, 3).has_edge(3, 4)); // deleted at start of window + assert!(!g.window(3, 4).has_edge(3, 4)); +} + +#[test] +fn test_deletions() { + let edges = [ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + let g = PersistentGraph::new(); + for (t, s, d) in edges.iter() { + g.add_edge(*t, *s, *d, NO_PROPS, None).unwrap(); + } + g.delete_edge(10, edges[0].1, edges[0].2, None).unwrap(); + + for (t, s, d) in &edges { + assert!(g.at(*t).has_edge(*s, *d)); + } + assert!(!g.after(10).has_edge(edges[0].1, edges[0].2)); + for (_, s, d) in &edges[1..] { + assert!(g.after(10).has_edge(*s, *d)); + } + assert_eq!( + g.edge(edges[0].1, edges[0].2) + .unwrap() + .explode() + .latest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(10)] + ); +} + +fn check_valid<'graph, G: GraphViewOps<'graph>>(e: &EdgeView) { + assert!(e.is_valid()); + assert!(!e.is_deleted()); + assert!(e.graph.has_edge(e.src(), e.dst())); + assert!(e.graph.edge(e.src(), e.dst()).is_some()); +} + +fn check_deleted<'graph, G: GraphViewOps<'graph>>(e: &EdgeView) { + assert!(!e.is_valid()); + assert!(e.is_deleted()); + let t = e.latest_time().map(|t| t.t()).unwrap_or(i64::MAX); + let g = e.graph.at(t); // latest view of the graph + assert!(!g.has_edge(e.src(), e.dst())); + assert!(g.edge(e.src(), e.dst()).is_none()); +} + +#[test] +fn test_deletion_multiple_layers() { + let g = PersistentGraph::new(); + + g.add_edge(1, 1, 2, NO_PROPS, Some("1")).unwrap(); + g.delete_edge(2, 1, 2, Some("2")).unwrap(); + g.delete_edge(10, 1, 2, Some("1")).unwrap(); + g.add_edge(10, 1, 2, NO_PROPS, Some("2")).unwrap(); + + let e = g.edge(1, 2).unwrap(); + let e_layer_1 = e.layers("1").unwrap(); + let e_layer_2 = e.layers("2").unwrap(); + + assert!(!g.at(0).has_edge(1, 2)); + check_deleted(&e.at(0)); + for t in 1..13 { + assert!(g.at(t).has_edge(1, 2)); + check_valid(&e.at(t)); + } + + check_valid(&e); + + check_deleted(&e_layer_1); + check_deleted(&e_layer_1.at(10)); + check_valid(&e_layer_1.at(9)); + check_valid(&e_layer_1.at(1)); + check_deleted(&e_layer_1.at(0)); + + check_valid(&e_layer_2); + check_deleted(&e_layer_2.at(9)); + check_valid(&e_layer_2.at(10)); +} + +#[test] +fn test_materialize_node_type() { + let g = PersistentGraph::new(); + g.delete_edge(0, 0, 0, None).unwrap(); + g.node(0).unwrap().set_node_type("test").unwrap(); + assert_graph_equal(&g, &g.materialize().unwrap()); +} + +#[test] +fn test_edge_is_valid() { + let g = PersistentGraph::new(); + + g.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + let e = g.edge(1, 2).unwrap(); + check_deleted(&e.before(1)); + check_valid(&e.after(1)); + check_valid(&e); + + g.add_edge(2, 1, 2, NO_PROPS, Some("1")).unwrap(); + check_valid(&e); + + g.delete_edge(3, 1, 2, Some("1")).unwrap(); + check_valid(&e); + check_deleted(&e.layers("1").unwrap()); + check_deleted(&e.layers("1").unwrap().at(3)); + check_deleted(&e.layers("1").unwrap().after(3)); + check_valid(&e.layers("1").unwrap().before(3)); + check_valid(&e.default_layer()); + + g.delete_edge(4, 1, 2, None).unwrap(); + check_deleted(&e); + check_deleted(&e.layers("1").unwrap()); + check_deleted(&e.default_layer()); + + g.add_edge(5, 1, 2, NO_PROPS, None).unwrap(); + check_valid(&e); + check_valid(&e.default_layer()); + check_deleted(&e.layers("1").unwrap()); +} + +/// Each layer is handled individually, deletions in one layer do not have an effect on other layers. +/// Layers that have only deletions are ignored +#[test] +fn test_explode_multiple_layers() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); + g.add_edge(1, 1, 2, NO_PROPS, Some("2")).unwrap(); + g.delete_edge(1, 1, 2, Some("1")).unwrap(); + g.delete_edge(2, 1, 2, Some("2")).unwrap(); + g.delete_edge(3, 1, 2, Some("3")).unwrap(); + + let e = g.edge(1, 2).unwrap(); + assert_eq!(e.explode().iter().count(), 2); + assert_eq!(e.before(4).explode().iter().count(), 2); + assert_eq!(e.window(1, 3).explode().iter().count(), 1); + assert_eq!(e.window(2, 3).explode().iter().count(), 0); +} + +#[test] +fn test_edge_latest_time() { + let g = PersistentGraph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e.delete(2, None).unwrap(); + assert_eq!(e.at(2).earliest_time(), None); + assert_eq!(e.at(2).latest_time(), None); + assert!(e.at(2).is_deleted()); + assert_eq!(e.latest_time().unwrap().t(), 2); + e.add_updates(4, NO_PROPS, None).unwrap(); + assert_eq!(e.latest_time().unwrap().t(), 4); + + assert_eq!(e.window(0, 3).latest_time().unwrap().t(), 2); +} + +#[test] +fn test_node_property_semantics() { + let g = PersistentGraph::new(); + let _v = g + .add_node(1, 1, [("test_prop", "test value")], None, None) + .unwrap(); + let v = g + .add_node(11, 1, [("test_prop", "test value 2")], None, None) + .unwrap(); + let v_from_graph = g.at(10).node(1).unwrap(); + assert_eq!(v.properties().get("test_prop").unwrap_str(), "test value 2"); + assert_eq!( + v.at(10).properties().get("test_prop").unwrap_str(), + "test value" + ); + assert_eq!( + v.at(11).properties().get("test_prop").unwrap_str(), + "test value 2" + ); + assert_eq!( + v_from_graph.properties().get("test_prop").unwrap_str(), + "test value" + ); + + assert_eq!( + v.before(11).properties().get("test_prop").unwrap_str(), + "test value" + ); + + assert_eq!( + v.properties() + .temporal() + .get("test_prop") + .unwrap() + .history(), + [1, 11] + ); + assert_eq!( + v_from_graph + .properties() + .temporal() + .get("test_prop") + .unwrap() + .history(), + [10] + ); + + assert_eq!(v_from_graph.earliest_time().map(|t| t.t()), Some(10)); + assert_eq!(v.earliest_time().map(|t| t.t()), Some(1)); + assert_eq!(v.at(10).earliest_time().map(|t| t.t()), Some(10)); + assert_eq!(v.at(10).latest_time().map(|t| t.t()), Some(10)); + assert_eq!(v.latest_time().map(|t| t.t()), Some(11)); +} + +#[test] +fn test_event_graph() { + let pg = PersistentGraph::new(); + pg.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) + .unwrap(); + pg.delete_edge(10, 0, 1, None).unwrap(); + assert_eq!( + pg.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); + + let g = pg.event_graph(); + assert_eq!( + g.edges().id().collect::>(), + vec![(GID::U64(0), GID::U64(1))] + ); +} + +#[test] +fn test_exploded_latest_time_deleted() { + let g = PersistentGraph::new(); + g.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + g.delete_edge(1, 1, 2, None).unwrap(); + + assert_eq!( + g.edge(1, 2) + .unwrap() + .explode() + .latest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(1)] + ); + assert_eq!( + g.edge(1, 2) + .unwrap() + .explode() + .earliest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(), + [Some(1)] + ) +} + +#[test] +fn test_empty_window_has_no_nodes() { + let g = PersistentGraph::new(); + g.add_node(1, 1, NO_PROPS, None, None).unwrap(); + assert_eq!(g.window(2, 2).count_nodes(), 0); + assert_eq!(g.window(1, 1).count_nodes(), 0); + assert_eq!(g.window(0, 0).count_nodes(), 0); +} + +// #[test] +// fn test_earliest_latest_only_deletion() { +// let g = PersistentGraph::new(); +// g.delete_edge(1, 1, 2, None).unwrap(); +// let gw = g.window(0, 1); +// assert_eq!(gw.earliest_time(), Some(0)); +// assert_eq!(gw.latest_time(), Some(0)); +// } + +#[test] +fn test_earliest_latest_time_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + + assert_eq!(g.window(-1, 0).earliest_time(), None); + assert_eq!(g.window(-1, 0).latest_time(), None); +} + +#[test] +fn test_node_earliest_time_window() { + let g = PersistentGraph::new(); + g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + g.add_edge(4, 1, 3, NO_PROPS, None).unwrap(); + + assert_eq!( + g.window(2, 7).node(3).unwrap().earliest_time().unwrap().t(), + 4 + ); + assert_eq!( + g.window(2, 7).node(1).unwrap().earliest_time().unwrap().t(), + 2 + ); +} + +#[test] +fn test_graph_property_semantics() { + let g = PersistentGraph::new(); + g.add_properties(1, [("weight", 10i64)]).unwrap(); + g.add_properties(3, [("weight", 20i64)]).unwrap(); + let prop = g.properties().temporal().get("weight").unwrap(); + + assert_eq!( + prop, + [(EventTime::new(1, 0), 10i64), (EventTime::new(3, 1), 20i64)] + ); + + let prop = g + .window(5, 7) + .properties() + .temporal() + .get("weight") + .unwrap(); + assert_eq!(prop, [(EventTime::new(5, 0), 20i64)]) +} + +#[test] +fn test_exploded_edge_window() { + let g = PersistentGraph::new(); + g.add_edge(1, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(3, 0, 1, [("test", 3i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let prop_values = g + .window(2, 5) + .edges() + .explode() + .properties() + .map(|props| props.get("test").unwrap_i64()) + .collect::>(); + assert_eq!(prop_values, [1, 3, 4]); +} + +/// This is a weird edge case +/// +/// An edge deletion creates the corresponding nodes so they should be alive from that point on. +/// We might consider changing the exact semantics here... +#[test] +fn test_node_earliest_latest_time_edge_deletion_only() { + let g = PersistentGraph::new(); + g.delete_edge(10, 0, 1, None).unwrap(); + assert_eq!(g.node(0).unwrap().earliest_time().unwrap().t(), 10); + assert_eq!(g.node(1).unwrap().earliest_time().unwrap().t(), 10); + assert_eq!(g.node(0).unwrap().latest_time().unwrap().t(), 10); + assert_eq!(g.node(1).unwrap().latest_time().unwrap().t(), 10); +} + +/// For an edge the earliest time is the time of the first update (either addition or deletion) +/// +/// The latest time is the time stamp of the last deletion if the last update is a deletion or +/// i64::MAX otherwise. +#[test] +fn test_edge_earliest_latest_time_edge_deletion_only() { + let g = PersistentGraph::new(); + g.delete_edge(10, 0, 1, None).unwrap(); + assert_eq!(g.edge(0, 1).unwrap().earliest_time().unwrap().t(), 10); + assert_eq!(g.edge(0, 1).unwrap().latest_time().unwrap().t(), 10); +} + +/// Repeated deletions are ignored, only the first one is relevant. Subsequent deletions do not +/// create exploded edges +#[test] +fn test_repeated_deletions() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(2, 0, 1, None).unwrap(); + g.delete_edge(4, 0, 1, None).unwrap(); + + let e = g.edge(0, 1).unwrap(); + let ex_earliest_t = e + .explode() + .earliest_time() + .map(|t_opt| t_opt.map(|t| t.t())) + .collect_vec(); + assert_eq!(ex_earliest_t, [Some(0)]); +} + +/// Only additions create exploded edges +#[test] +fn test_only_deletions() { + let g = PersistentGraph::new(); + g.delete_edge(2, 0, 1, None).unwrap(); + g.delete_edge(4, 0, 1, None).unwrap(); + + let e = g.edge(0, 1).unwrap(); + let ex_earliest_t = e.explode().earliest_time().collect_vec(); + assert_eq!(ex_earliest_t, []); +} + +/// Deletions only bring an edge into the window if they fall inside the window, not if they fall +/// onto the start of the window. The edge is already considered deleted at the time of the +/// deletion event, not at the next step. +#[test] +fn test_only_deletions_window() { + let g = PersistentGraph::new(); + g.delete_edge(1, 0, 1, None).unwrap(); + + // the edge exists + assert!(g.has_edge(0, 1)); + + // the deletion falls inside the window so the edge exists + assert!(g.window(0, 2).has_edge(0, 1)); + + // the deletion falls on the start of the window so the edge is already deleted and does not exist + assert!(!g.window(1, 2).has_edge(0, 1)); + + // windows that don't contain the deletion event don't have the edge + assert!(!g.window(0, 1).has_edge(0, 1)); + assert!(!g.window(2, 3).has_edge(0, 1)); +} + +#[test] +fn test_multiple_updates_at_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(0, 0, 1, [("test", 2i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap(); + assert_eq!(e.properties().get("test").unwrap_i64(), 2); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(0, Prop::I64(1)), (0, Prop::I64(2))] + ); + + assert_eq!(e.at(0).properties().get("test").unwrap_i64(), 2); + assert_eq!( + e.at(0) + .properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(0, Prop::I64(1)), (0, Prop::I64(2))] + ); + + assert_eq!( + e.at(0) + .explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [1, 2] + ); + assert_eq!(e.at(0).history(), [0, 0]); + assert_eq!(e.history(), [0, 0]); +} + +#[test] +fn no_persistence_if_updated_at_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap().window(2, 5); + + assert_eq!(e.properties().get("test").unwrap_i64(), 4); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(2, Prop::I64(2)), (4, Prop::I64(4))] + ); + assert_eq!( + e.explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [2, 4] + ); + assert_eq!(e.history(), [2, 4]); + assert!(e.deletions().is_empty()); +} + +#[test] +fn persistence_if_not_updated_at_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap().window(1, 5); + + assert_eq!(e.properties().get("test").unwrap_i64(), 4); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(1, Prop::I64(1)), (2, Prop::I64(2)), (4, Prop::I64(4))] + ); + assert_eq!( + e.explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [1, 2, 4] + ); + assert_eq!(e.history(), [2, 4]); // is this actually what we want? + assert!(e.deletions().is_empty()); + assert_eq!(g.window(1, 5).count_temporal_edges(), 3); + assert_eq!(g.window(2, 5).count_temporal_edges(), 2); + assert_eq!(g.window(3, 5).count_temporal_edges(), 2); +} + +#[test] +fn no_persistence_if_deleted() { + let g = PersistentGraph::new(); + g.add_edge(-1, 0, 1, [("test", 1i64)], None).unwrap(); + g.delete_edge(0, 0, 1, None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap().window(1, 5); + + assert_eq!(e.properties().get("test").unwrap_i64(), 4); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(2, Prop::I64(2)), (4, Prop::I64(4))] + ); + assert_eq!( + e.explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [2, 4] + ); + assert_eq!(e.history(), [2, 4]); + assert!(e.deletions().is_empty()); + assert_eq!(g.window(0, 5).count_temporal_edges(), 2); + assert_eq!(g.window(1, 5).count_temporal_edges(), 2); + assert_eq!(g.window(3, 5).count_temporal_edges(), 2); +} + +#[test] +fn test_deletion_at_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); + g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); + g.delete_edge(2, 0, 1, None).unwrap(); + g.add_edge(2, 0, 1, [("test", 3i64)], None).unwrap(); + g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); + + let e = g.edge(0, 1).unwrap().window(2, 5); + + assert_eq!(e.properties().get("test").unwrap_i64(), 4); + assert_eq!( + e.properties() + .temporal() + .get("test") + .unwrap() + .iter() + .map(|(t, p)| (t.t(), p)) + .collect_vec(), + [(2, Prop::I64(3)), (4, Prop::I64(4))] + ); + + assert_eq!( + e.explode() + .properties() + .map(|p| p.get("test").unwrap_i64()) + .collect_vec(), + [3, 4] + ); + + assert!(e.deletions().is_empty()); + assert_eq!(e.history(), [2, 4]); + assert_eq!(g.window(1, 5).count_temporal_edges(), 4); + assert_eq!(g.window(2, 5).count_temporal_edges(), 2); + assert_eq!(g.window(3, 5).count_temporal_edges(), 2); +} + +#[test] +fn multiple_node_updates_at_same_time() { + let g = PersistentGraph::new(); + + g.add_node(1, 1, [("prop1", 1)], None, None).unwrap(); + g.add_node(2, 1, [("prop1", 2)], None, None).unwrap(); + g.add_node(2, 1, [("prop1", 3)], None, None).unwrap(); + g.add_node(8, 1, [("prop1", 4)], None, None).unwrap(); + g.add_node(9, 1, [("prop1", 5)], None, None).unwrap(); + + assert_eq!( + g.window(2, 10) + .node(1) + .unwrap() + .properties() + .temporal() + .get("prop1") + .unwrap() + .values() + .collect_vec(), + [Prop::I32(2), Prop::I32(3), Prop::I32(4), Prop::I32(5)] + ) +} + +#[test] +fn filtering_all_layers_keeps_explicitly_added_nodes() { + let g = PersistentGraph::new(); + g.add_node(0, 0, [("prop1", false)], None, None).unwrap(); + let view = g.valid_layers(Layer::None).window(0, 1); + assert_eq!(view.count_nodes(), 1); + assert_eq!(view.count_edges(), 0); + assert_graph_equal(&view, &view.materialize().unwrap()) +} + +#[test] +fn filtering_all_layers_removes_other_nodes() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + let view = g.valid_layers(Layer::None).window(0, 1); + assert_eq!(view.count_nodes(), 0); + assert_eq!(view.count_edges(), 0); + assert_graph_equal(&view, &view.materialize().unwrap()) +} + +#[test] +fn deletions_window_has_exclusive_start() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(2, 0, 1, None).unwrap(); + let e = g.edge(0, 1).unwrap(); + assert!(e.is_active()); // has updates + assert!(!e.is_valid()); // last update is a deletion + assert!(e.is_deleted()); + + assert!(e.window(0, 1).is_active()); // addition in window + assert!(e.window(0, 1).is_valid()); // not deleted + assert!(!e.window(0, 1).is_deleted()); + + assert!(e.window(1, 3).is_active()); // deletion in window + assert!(!e.window(1, 3).is_valid()); + assert!(e.window(1, 3).is_deleted()); + + assert!(!e.window(1, 2).is_active()); // no updates in window + assert!(e.window(1, 2).is_valid()); // deletion not in window (exclusive end) + assert!(!e.window(1, 2).is_deleted()); + + assert!(!e.window(2, 3).is_active()); // deletion at start of window are not included + assert!(!e.window(2, 3).is_valid()); + assert!(e.window(2, 3).is_deleted()); + assert!(!e.latest().is_active()); // this is the same as above +} diff --git a/raphtory-test-utils/tests/test_edge.rs b/raphtory-test-utils/tests/test_edge.rs new file mode 100644 index 0000000000..85a8499823 --- /dev/null +++ b/raphtory-test-utils/tests/test_edge.rs @@ -0,0 +1,189 @@ +use itertools::Itertools; +use raphtory::{ + db::{ + api::view::Filter, + graph::views::filter::model::{EdgeViewFilterOps, ViewWrapOps}, + }, + prelude::*, +}; +use raphtory_api::core::{ + entities::LayerId, + storage::{arc_str::ArcStr, timeindex::AsTime}, +}; +use raphtory_test_utils::{test_storage, test_utils::test_graph}; +use std::collections::HashMap; + +#[test] +fn test_properties() { + let graph = Graph::new(); + let props = [(ArcStr::from("test"), "test".into_prop())]; + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 2, props.clone(), None).unwrap(); + test_storage!(&graph, |graph| { + let e1 = graph.edge(1, 2).unwrap(); + let e1_w = graph.window(0, 1).edge(1, 2).unwrap(); + assert_eq!( + HashMap::from_iter(e1.properties().as_vec()), + props.into_iter().collect::>() + ); + assert!(e1_w.properties().as_vec().is_empty()) + }); +} + +#[test] +fn test_metadata() { + let graph = Graph::new(); + graph + .add_edge(1, 1, 2, NO_PROPS, Some("layer 1")) + .unwrap() + .add_metadata([("test_prop", "test_val")], None) + .unwrap(); + graph + .add_edge(1, 2, 3, NO_PROPS, Some("layer 2")) + .unwrap() + .add_metadata([("test_prop", "test_val"), ("other", "2")], None) + .unwrap(); + + graph + .add_edge(1, 2, 3, NO_PROPS, Some("layer 3")) + .unwrap() + .add_metadata([("test_prop", "test_val"), ("other", "3")], None) + .unwrap(); + + // FIXME: #18 metadata prop for edges + test_graph(&graph, |graph| { + assert_eq!( + graph.edge(1, 2).unwrap().metadata().get("test_prop"), + Some(Prop::map([("layer 1", "test_val")])) + ); + assert_eq!( + graph.edge(2, 3).unwrap().metadata().get("test_prop"), + Some(Prop::map([ + ("layer 2", "test_val"), + ("layer 3", "test_val") + ])) + ); + + assert_eq!( + graph.edge(2, 3).unwrap().metadata().get("other"), + Some(Prop::map([("layer 2", "2"), ("layer 3", "3")])) + ); + + assert_eq!( + graph + .valid_layers(["layer 3", "layer 2"]) + .edge(2, 3) + .unwrap() + .metadata() + .get("other"), + Some(Prop::map([("layer 2", "2"), ("layer 3", "3")])) + ); + + for e in graph.edges() { + for ee in e.explode() { + assert_eq!(ee.metadata().get("test_prop"), Some("test_val".into())) + } + } + }); +} + +#[test] +fn test_property_additions() { + let graph = Graph::new(); + let props = [("test", "test")]; + let e1 = graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + e1.add_updates(2, props, None).unwrap(); // same layer works + assert!(e1.add_updates(2, props, Some("test2")).is_err()); // different layer is error + let e = graph.edge(1, 2).unwrap(); + e.add_updates(2, props, Some("test2")).unwrap(); // non-restricted edge view can create new layers + let layered_views = e.explode_layers().into_iter().collect_vec(); + for ev in layered_views { + let layer = ev.layer_name().unwrap(); + assert!(ev.add_updates(1, props, Some("test")).is_err()); // restricted edge view cannot create updates in different layer + ev.add_updates(1, [("test2", layer)], None).unwrap() // this will add an update to the same layer as the view (not the default layer) + } + + let e1_w = e1.window(0, 1); + assert_eq!( + e1.properties().as_map(), + props + .into_iter() + .map(|(k, v)| (ArcStr::from(k), v.into_prop())) + .chain([(ArcStr::from("test2"), "_default".into_prop())]) + .collect::>() + ); + assert_eq!( + e.layers("test2").unwrap().properties().as_map(), + props + .into_iter() + .map(|(k, v)| (ArcStr::from(k), v.into_prop())) + .chain([(ArcStr::from("test2"), "test2".into_prop())]) + .collect::>() + ); + assert_eq!(e1_w.properties().as_map(), HashMap::default()) +} + +#[test] +fn test_metadata_additions() { + let g = Graph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); + assert_eq!(e.edge.layer(), Some(LayerId(1))); // 0 is static graph + assert!(e.add_metadata([("test1", "test1")], None).is_ok()); // adds properties to layer `"test"` + assert!(e.add_metadata([("test", "test")], Some("test2")).is_err()); // cannot add properties to a different layer + e.add_metadata([("test", "test")], Some("test")).unwrap(); // layer is consistent + assert_eq!(e.metadata().get("test"), Some("test".into())); + assert_eq!(e.metadata().get("test1"), Some("test1".into())); +} + +#[test] +fn test_metadata_updates() { + let g = Graph::new(); + let e = g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); + assert!(e.add_metadata([("test1", "test1")], None).is_ok()); // adds properties to layer `"test"` + assert!(e.update_metadata([("test1", "test2")], None).is_ok()); + assert_eq!(e.metadata().get("test1"), Some("test2".into())); +} + +#[test] +fn test_layers_earliest_time() { + let g = Graph::new(); + let e = g.add_edge(1, 1, 2, NO_PROPS, Some("test")).unwrap(); + assert_eq!(e.earliest_time().map(|t| t.t()), Some(1)); +} + +#[test] +fn test_edge_layers() { + let graph = Graph::new(); + + graph + .add_edge(0, 1, 2, NO_PROPS, Some("fire_nation")) + .unwrap(); + graph + .add_edge(0, 2, 3, [("a", "test")], Some("fire_nation")) + .unwrap(); + graph + .add_edge(0, 3, 4, [("b", 4)], Some("air_nomads")) + .unwrap(); + + let filter_expr = EdgeFilter.layer("fire_nation").is_active(); + let ids = graph + .filter(filter_expr) + .unwrap() + .edges() + .iter() + .map(|n| n.src().name() + "->" + &n.dst().name()) + .collect::>(); + + assert_eq!(ids, ["1->2", "2->3"]); + + let filter_expr = EdgeFilter.layer("air_nomads").is_active(); + let ids = graph + .filter(filter_expr) + .unwrap() + .edges() + .iter() + .map(|n| n.src().name() + "->" + &n.dst().name()) + .collect::>(); + + assert_eq!(ids, ["3->4"]); +} diff --git a/raphtory-test-utils/tests/test_edge_view.rs b/raphtory-test-utils/tests/test_edge_view.rs new file mode 100644 index 0000000000..b4a53a2e34 --- /dev/null +++ b/raphtory-test-utils/tests/test_edge_view.rs @@ -0,0 +1,162 @@ +use raphtory::prelude::*; +use raphtory_test_utils::{test_storage, test_utils::test_graph}; + +#[test] +fn test_exploded_edge_properties() { + let graph = Graph::new(); + let actual_prop_values = vec![0, 1, 2, 3]; + for v in actual_prop_values.iter() { + graph.add_edge(0, 1, 2, [("test", *v)], None).unwrap(); + } + + test_storage!(&graph, |graph| { + let prop_values: Vec<_> = graph + .edge(1, 2) + .unwrap() + .explode() + .properties() + .flat_map(|p| p.get("test").into_i32()) + .collect(); + assert_eq!(prop_values, actual_prop_values) + }); +} + +#[test] +fn test_exploded_edge_properties_window() { + let graph = Graph::new(); + let actual_prop_values_0 = vec![0, 1, 2, 3]; + for v in actual_prop_values_0.iter() { + graph.add_edge(0, 1, 2, [("test", *v)], None).unwrap(); + } + let actual_prop_values_1 = vec![4, 5, 6]; + for v in actual_prop_values_1.iter() { + graph.add_edge(1, 1, 2, [("test", *v)], None).unwrap(); + } + test_storage!(&graph, |graph| { + let prop_values: Vec<_> = graph + .at(0) + .edge(1, 2) + .unwrap() + .explode() + .properties() + .flat_map(|p| p.get("test").into_i32()) + .collect(); + assert_eq!(prop_values, actual_prop_values_0); + let prop_values: Vec<_> = graph + .at(1) + .edge(1, 2) + .unwrap() + .explode() + .properties() + .flat_map(|p| p.get("test").into_i32()) + .collect(); + assert_eq!(prop_values, actual_prop_values_1) + }); +} + +#[test] +fn test_exploded_edge_multilayer() { + let graph = Graph::new(); + let expected_prop_values = vec![0, 1, 2, 3]; + for v in expected_prop_values.iter() { + graph + .add_edge(0, 1, 2, [("test", *v)], Some((v % 2).to_string().as_str())) + .unwrap(); + } + // FIXME: Needs support for event id from EventTime in disk storage (Issue #30) + test_graph(&graph, |graph| { + let prop_values: Vec<_> = graph + .edge(1, 2) + .unwrap() + .explode() + .properties() + .flat_map(|p| p.get("test").into_i32()) + .collect(); + let actual_layers: Vec<_> = graph + .edge(1, 2) + .unwrap() + .explode() + .layer_name() + .flatten() + .collect(); + let expected_layers: Vec<_> = expected_prop_values + .iter() + .map(|v| (v % 2).to_string()) + .collect(); + assert_eq!(prop_values, expected_prop_values); + assert_eq!(actual_layers, expected_layers); + + assert!(graph.edge(1, 2).unwrap().layer_name().is_err()); + assert!(graph.edges().layer_name().all(|l| l.is_err())); + assert!(graph + .edge(1, 2) + .unwrap() + .explode() + .layer_name() + .all(|l| l.is_ok())); + assert!(graph + .edge(1, 2) + .unwrap() + .explode_layers() + .layer_name() + .all(|l| l.is_ok())); + assert!(graph.edges().explode().layer_name().all(|l| l.is_ok())); + assert!(graph + .edges() + .explode_layers() + .layer_name() + .all(|l| l.is_ok())); + + assert!(graph.edge(1, 2).unwrap().time().is_err()); + assert!(graph.edges().time().all(|l| l.is_err())); + assert!(graph + .edge(1, 2) + .unwrap() + .explode() + .time() + .all(|l| l.is_ok())); + assert!(graph + .edge(1, 2) + .unwrap() + .explode_layers() + .time() + .all(|l| l.is_err())); + assert!(graph.edges().explode().time().all(|l| l.is_ok())); + assert!(graph.edges().explode_layers().time().all(|l| l.is_err())); + }); +} + +#[test] +fn test_sorting_by_event_id() { + let graph = Graph::new(); + graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(0, 1, 2, [("second", true)], None).unwrap(); + graph.add_edge(0, 2, 3, [("second", true)], None).unwrap(); + + //FIXME: DiskGraph does not preserve event id (see #1780) + test_graph(&graph, |graph| { + let mut exploded_edges: Vec<_> = graph.edges().explode().into_iter().collect(); + exploded_edges.sort_by_key(|a| a.time_and_event_id().unwrap()); + + let res: Vec<_> = exploded_edges + .into_iter() + .filter_map(|e| { + Some(( + e.src().id().as_u64()?, + e.dst().id().as_u64()?, + e.properties().get("second").into_bool(), + )) + }) + .collect(); + assert_eq!( + res, + vec![ + (2, 3, None), + (1, 2, None), + (1, 2, Some(true)), + (2, 3, Some(true)) + ] + ) + }); +} diff --git a/raphtory-test-utils/tests/test_exploded_edges.rs b/raphtory-test-utils/tests/test_exploded_edges.rs new file mode 100644 index 0000000000..32364f78ff --- /dev/null +++ b/raphtory-test-utils/tests/test_exploded_edges.rs @@ -0,0 +1,551 @@ +use itertools::Itertools; +use raphtory::prelude::*; +use raphtory_test_utils::test_storage; + +#[test] +fn test_add_node_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph + .add_node((0, 3), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); +} + +#[test] +fn test_add_node_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph + .add_node((0, 1), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node(0) + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph + .add_node((0, 1), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node(0) + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); +} + +#[test] +fn test_create_node_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph + .create_node((0, 3), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); +} + +#[test] +fn test_create_node_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph + .create_node((0, 1), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 1), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node(0) + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph + .create_node((0, 1), 0, [("prop", "1")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "2")], None, None) + .unwrap(); + graph + .add_node((0, 2), 0, [("prop", "3")], None, None) + .unwrap(); + + let props = graph + .node(0) + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); +} + +#[test] +fn test_add_edge_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph.add_edge((0, 3), 0, 1, [("prop", "1")], None).unwrap(); + graph.add_edge((0, 2), 0, 1, [("prop", "2")], None).unwrap(); + graph.add_edge((0, 1), 0, 1, [("prop", "3")], None).unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); +} + +#[test] +fn test_add_edge_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph.add_edge((0, 1), 0, 1, [("prop", "1")], None).unwrap(); + graph.add_edge((0, 1), 0, 1, [("prop", "2")], None).unwrap(); + graph.add_edge((0, 1), 0, 1, [("prop", "3")], None).unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph.add_edge((0, 1), 0, 1, [("prop", "1")], None).unwrap(); + graph.add_edge((0, 2), 0, 1, [("prop", "2")], None).unwrap(); + graph.add_edge((0, 2), 0, 1, [("prop", "3")], None).unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); +} + +#[test] +fn test_add_properties_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph.add_properties((0, 3), [("prop", "1")]).unwrap(); + graph.add_properties((0, 2), [("prop", "2")]).unwrap(); + graph.add_properties((0, 1), [("prop", "3")]).unwrap(); + + let props = graph + .properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); +} + +#[test] +fn test_add_properties_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph.add_properties((0, 1), [("prop", "1")]).unwrap(); + graph.add_properties((0, 1), [("prop", "2")]).unwrap(); + graph.add_properties((0, 1), [("prop", "3")]).unwrap(); + + let props = graph + .properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph.add_edge((0, 1), 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge((0, 2), 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge((0, 2), 0, 1, NO_PROPS, None).unwrap(); + + graph.add_properties((0, 1), [("prop", "1")]).unwrap(); + graph.add_properties((0, 2), [("prop", "2")]).unwrap(); + graph.add_properties((0, 2), [("prop", "3")]).unwrap(); + + let props = graph + .properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); +} + +#[test] +fn test_node_add_updates_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + + graph + .node(0) + .unwrap() + .add_updates((0, 3), [("prop", "1")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 2), [("prop", "2")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "3")], None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); +} + +#[test] +fn test_node_add_updates_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "1")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "2")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "3")], None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + + graph + .node(0) + .unwrap() + .add_updates((0, 1), [("prop", "1")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 2), [("prop", "2")], None) + .unwrap(); + graph + .node(0) + .unwrap() + .add_updates((0, 2), [("prop", "3")], None) + .unwrap(); + + let props = graph + .node("0") + .map(|node| { + node.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); +} + +#[test] +fn test_edge_add_updates_properties_ordered_by_event_id() { + let graph: Graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 3), [("prop", "1")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 2), [("prop", "2")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "3")], None) + .unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!( + props, + vec!["3".to_string(), "2".to_string(), "1".to_string()] + ); +} + +#[test] +fn test_edge_add_updates_properties_overwritten_for_same_event_id() { + let graph: Graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "1")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "2")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "3")], None) + .unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["3".to_string()]); + + let graph: Graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 1), [("prop", "1")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 2), [("prop", "2")], None) + .unwrap(); + graph + .edge(0, 1) + .unwrap() + .add_updates((0, 2), [("prop", "3")], None) + .unwrap(); + + let props = graph + .edge(0, 1) + .map(|edge| { + edge.properties() + .temporal() + .get("prop") + .unwrap() + .values() + .map(|x| x.to_string()) + .collect_vec() + }) + .unwrap(); + + assert_eq!(props, vec!["1".to_string(), "3".to_string()]); +} + +#[test] +fn test_exploded_edges() { + let graph: Graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(1, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(2, 0, 1, NO_PROPS, None).unwrap(); + graph.add_edge(3, 0, 1, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + assert_eq!(graph.count_temporal_edges(), 4) + }); +} diff --git a/raphtory-test-utils/tests/test_filters.rs b/raphtory-test-utils/tests/test_filters.rs new file mode 100644 index 0000000000..156c793d3e --- /dev/null +++ b/raphtory-test-utils/tests/test_filters.rs @@ -0,0 +1,12325 @@ +use raphtory::{db::api::view::StaticGraphViewOps, prelude::*}; + +#[cfg(test)] +mod test_composite_filters { + use raphtory::{ + db::graph::views::filter::model::{ + edge_filter::EdgeFilter, filter::Filter, node_filter::NodeFilter, + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }, + prelude::IntoProp, + }; + use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; + + #[test] + fn test_fuzzy_search() { + let filter = Filter::fuzzy_search("name", "pomet", 2, false); + assert!(filter.matches(Some("pometry"))); + + let filter = Filter::fuzzy_search("name", "shivam_kapoor", 2, false); + assert!(filter.matches(Some("shivam_kapoor2"))); + + let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); + assert!(filter.matches(Some("shivam_kapoor2"))); + + let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); + assert!(filter.matches(Some("shivam_kapoor2"))); + + let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); + assert!(!filter.matches(Some("shivam1_kapoor2"))); + + let filter = Filter::fuzzy_search("name", "khivam sapoor", 2, false); + assert!(!filter.matches(Some("shivam1_kapoor2"))); + } + + #[test] + fn test_fuzzy_search_prefix_match() { + let filter = Filter::fuzzy_search("name", "pome", 2, false); + assert!(!filter.matches(Some("pometry"))); + + let filter = Filter::fuzzy_search("name", "pome", 2, true); + assert!(filter.matches(Some("pometry"))); + } + + #[test] + fn test_fuzzy_search_property() { + let filter = NodeFilter.property("prop").fuzzy_search("pomet", 2, false); + assert!(filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); + } + + #[test] + fn test_fuzzy_search_property_prefix_match() { + let filter = EdgeFilter.property("prop").fuzzy_search("pome", 2, false); + assert!(!filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); + + let filter = EdgeFilter.property("prop").fuzzy_search("pome", 2, true); + assert!(filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); + } + + #[test] + fn test_contains_match() { + let filter = EdgeFilter.property("prop").contains("shivam"); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); + assert!(res); + let res = filter.matches(None); + assert!(!res); + + let filter = EdgeFilter.property("prop").contains("am_ka"); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); + assert!(res); + } + + #[test] + fn test_contains_not_match() { + let filter = NodeFilter.property("prop").not_contains("shivam"); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); + assert!(!res); + let res = filter.matches(None); + assert!(!res); + } + + #[test] + fn test_is_in_match() { + let filter = NodeFilter + .property("prop") + .is_in(vec!["shivam".into_prop()]); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam")))); + assert!(res); + let res = filter.matches(None); + assert!(!res); + } + + #[test] + fn test_is_not_in_match() { + let filter = EdgeFilter + .property("prop") + .is_not_in(vec!["shivam".into_prop()]); + let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam")))); + assert!(!res); + let res = filter.matches(None); + assert!(!res); + } +} + +use raphtory_api::core::entities::properties::prop::IntoProp; +use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, +}; +use raphtory_test_utils::assertions::GraphTransformer; + +struct IdentityGraphTransformer; + +impl GraphTransformer for IdentityGraphTransformer { + type Return = G; + fn apply(&self, graph: G) -> Self::Return { + graph + } +} + +#[cfg(test)] +mod test_property_semantics { + #[cfg(test)] + mod test_node_property_filter_semantics { + use crate::IdentityGraphTransformer; + use raphtory::{ + db::{ + api::view::{filter_ops::Filter, StaticGraphViewOps}, + graph::views::filter::model::{ + node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, TemporalPropertyFilterFactory, + }, + }, + errors::GraphError, + prelude::*, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, + }; + use raphtory_test_utils::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestVariants, + }; + + fn init_graph(graph: G) -> G { + let nodes = [ + (6, "N1", vec![("p1", Prop::U64(2u64))]), + (7, "N1", vec![("p1", Prop::U64(1u64))]), + (6, "N2", vec![("p1", Prop::U64(1u64))]), + (7, "N2", vec![("p1", Prop::U64(2u64))]), + (8, "N3", vec![("p1", Prop::U64(1u64))]), + (9, "N4", vec![("p1", Prop::U64(1u64))]), + (5, "N5", vec![("p1", Prop::U64(1u64))]), + (6, "N5", vec![("p1", Prop::U64(2u64))]), + (5, "N6", vec![("p1", Prop::U64(1u64))]), + (6, "N6", vec![("p1", Prop::U64(1u64))]), + (3, "N7", vec![("p1", Prop::U64(1u64))]), + (5, "N7", vec![("p1", Prop::U64(1u64))]), + (3, "N8", vec![("p1", Prop::U64(1u64))]), + (4, "N8", vec![("p1", Prop::U64(2u64))]), + (2, "N9", vec![("p1", Prop::U64(2u64))]), + (2, "N10", vec![("q1", Prop::U64(0u64))]), + (2, "N10", vec![("p1", Prop::U64(3u64))]), + (2, "N11", vec![("p1", Prop::U64(3u64))]), + (2, "N11", vec![("q1", Prop::U64(0u64))]), + (2, "N12", vec![("q1", Prop::U64(0u64))]), + (3, "N12", vec![("p1", Prop::U64(3u64))]), + (2, "N13", vec![("q1", Prop::U64(0u64))]), + (3, "N13", vec![("p1", Prop::U64(3u64))]), + (2, "N14", vec![("q1", Prop::U64(0u64))]), + (2, "N15", vec![]), + ]; + + for (id, label, props) in nodes.iter() { + graph + .add_node(*id, label, props.clone(), None, None) + .unwrap(); + } + + let metadata = [ + ("N1", [("p1", Prop::U64(1u64))]), + ("N4", [("p1", Prop::U64(2u64))]), + ("N9", [("p1", Prop::U64(1u64))]), + ("N10", [("p1", Prop::U64(1u64))]), + ("N11", [("p1", Prop::U64(1u64))]), + ("N12", [("p1", Prop::U64(1u64))]), + ("N13", [("p1", Prop::U64(1u64))]), + ("N14", [("p1", Prop::U64(1u64))]), + ("N15", [("p1", Prop::U64(1u64))]), + ]; + + for (node, props) in metadata.iter() { + graph + .node(node) + .unwrap() + .add_metadata(props.clone()) + .unwrap(); + } + + graph + } + + fn init_graph_for_event_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let graph: G = init_graph(graph); + let nodes = [ + (1, "N16", vec![("p1", Prop::U64(2u64))]), + (1, "N16", vec![("p1", Prop::U64(1u64))]), + (1, "N17", vec![("p1", Prop::U64(1u64))]), + (1, "N17", vec![("p1", Prop::U64(2u64))]), + ]; + + for (id, label, props) in nodes.iter() { + graph + .add_node(*id, label, props.clone(), None, None) + .unwrap(); + } + + graph + } + + #[test] + fn test_metadata_semantics() { + let filter = NodeFilter.metadata("p1").eq(1u64); + let expected_results = vec!["N1", "N10", "N11", "N12", "N13", "N14", "N15", "N9"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_temporal_any_semantics() { + let filter = NodeFilter.property("p1").temporal().any().eq(1u64); + let expected_results = vec!["N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_temporal_any_semantics_for_event_ids() { + let filter = NodeFilter.property("p1").temporal().any().eq(1u64); + let expected_results = + vec!["N1", "N16", "N17", "N2", "N3", "N4", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_temporal_latest_semantics() { + let filter = NodeFilter.property("p1").temporal().last().eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_temporal_latest_semantics_for_event_ids() { + let filter = NodeFilter.property("p1").temporal().last().eq(1u64); + let expected_results = vec!["N1", "N16", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_property_semantics() { + // TODO: Const properties not supported for disk_graph. + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_property_semantics_for_event_ids() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N16", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_property_semantics_only_metadata() { + // For this graph there won't be any temporal property index for property name "p1". + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes = [(2, "N1", vec![("q1", Prop::U64(0u64))]), (2, "N2", vec![])]; + + for (id, label, props) in nodes.iter() { + graph + .add_node(*id, label, props.clone(), None, None) + .unwrap(); + } + + let metadata = [ + ("N1", [("p1", Prop::U64(1u64))]), + ("N2", [("p1", Prop::U64(1u64))]), + ]; + + for (node, props) in metadata.iter() { + graph + .node(node) + .unwrap() + .add_metadata(props.clone()) + .unwrap(); + } + + graph + } + + let filter = NodeFilter.property("p1").ge(1u64); + let graph = init_graph(Graph::new()); + assert!(matches!( + graph.filter(filter.clone()).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "p1" + )); + assert!(matches!( + graph.persistent_graph().filter(filter).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "p1" + )); + } + + #[test] + fn test_property_semantics_only_temporal() { + // For this graph there won't be any metadata index for property name "p1". + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes = [ + (1, "N1", vec![("p1", Prop::U64(1u64))]), + (2, "N2", vec![("p1", Prop::U64(1u64))]), + (3, "N2", vec![("p1", Prop::U64(2u64))]), + (2, "N3", vec![("p1", Prop::U64(2u64))]), + (3, "N3", vec![("p1", Prop::U64(1u64))]), + (3, "N4", vec![]), + ]; + + for (id, label, props) in nodes.iter() { + graph + .add_node(*id, label, props.clone(), None, None) + .unwrap(); + } + + graph + } + + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N1", "N3"]; + assert_filter_nodes_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + } + + #[cfg(test)] + mod test_edge_property_filter_semantics { + use crate::IdentityGraphTransformer; + use raphtory::{ + db::{ + api::view::{filter_ops::Filter, EdgeViewOps, StaticGraphViewOps}, + graph::views::filter::{ + model::{ + edge_filter::EdgeFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, TemporalPropertyFilterFactory, + }, + CreateFilter, + }, + }, + errors::GraphError, + prelude::*, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, + }; + use raphtory_test_utils::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, + TestVariants, WindowGraphTransformer, + }; + + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + (6, "N1", "N2", vec![("p1", Prop::U64(2u64))]), + (7, "N1", "N2", vec![("p1", Prop::U64(1u64))]), + (6, "N2", "N3", vec![("p1", Prop::U64(1u64))]), + (7, "N2", "N3", vec![("p1", Prop::U64(2u64))]), + (8, "N3", "N4", vec![("p1", Prop::U64(1u64))]), + (9, "N4", "N5", vec![("p1", Prop::U64(1u64))]), + (5, "N5", "N6", vec![("p1", Prop::U64(1u64))]), + (6, "N5", "N6", vec![("p1", Prop::U64(2u64))]), + (5, "N6", "N7", vec![("p1", Prop::U64(1u64))]), + (6, "N6", "N7", vec![("p1", Prop::U64(1u64))]), + (3, "N7", "N8", vec![("p1", Prop::U64(1u64))]), + (5, "N7", "N8", vec![("p1", Prop::U64(1u64))]), + (3, "N8", "N9", vec![("p1", Prop::U64(1u64))]), + (4, "N8", "N9", vec![("p1", Prop::U64(2u64))]), + (2, "N9", "N10", vec![("p1", Prop::U64(2u64))]), + (2, "N10", "N11", vec![("q1", Prop::U64(0u64))]), + (2, "N10", "N11", vec![("p1", Prop::U64(3u64))]), + (2, "N11", "N12", vec![("p1", Prop::U64(3u64))]), + (2, "N11", "N12", vec![("q1", Prop::U64(0u64))]), + (2, "N12", "N13", vec![("q1", Prop::U64(0u64))]), + (3, "N12", "N13", vec![("p1", Prop::U64(3u64))]), + (2, "N13", "N14", vec![("q1", Prop::U64(0u64))]), + (3, "N13", "N14", vec![("p1", Prop::U64(3u64))]), + (2, "N14", "N15", vec![("q1", Prop::U64(0u64))]), + (2, "N15", "N1", vec![]), + ]; + + for (time, src, dst, props) in edges { + graph.add_edge(time, src, dst, props, None).unwrap(); + } + + let metadata_edges = [ + ("N1", "N2", vec![("p1", Prop::U64(1u64))]), + ("N4", "N5", vec![("p1", Prop::U64(2u64))]), + ("N9", "N10", vec![("p1", Prop::U64(1u64))]), + ("N10", "N11", vec![("p1", Prop::U64(1u64))]), + ("N11", "N12", vec![("p1", Prop::U64(1u64))]), + ("N12", "N13", vec![("p1", Prop::U64(1u64))]), + ("N13", "N14", vec![("p1", Prop::U64(1u64))]), + ("N14", "N15", vec![("p1", Prop::U64(1u64))]), + ("N15", "N1", vec![("p1", Prop::U64(1u64))]), + ]; + + for (src, dst, props) in metadata_edges { + graph + .edge(src, dst) + .unwrap() + .add_metadata(props.clone(), None) + .unwrap(); + } + + graph + } + + fn init_graph_for_event_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let graph: G = init_graph(graph); + let edge_data = [ + (1, "N16", "N15", vec![("p1", Prop::U64(2u64))]), + (1, "N16", "N15", vec![("p1", Prop::U64(1u64))]), + (1, "N17", "N16", vec![("p1", Prop::U64(1u64))]), + (1, "N17", "N16", vec![("p1", Prop::U64(2u64))]), + ]; + + for (time, src, dst, props) in edge_data { + graph.add_edge(time, src, dst, props, None).unwrap(); + } + + graph + } + + #[test] + fn test_persistent_graph_first_window() { + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + graph + .add_edge(0, 1, 2, [("p1", Prop::U64(1u64))], None) + .unwrap(); + graph + .add_edge(2, 1, 2, [("p1", Prop::U64(2u64))], None) + .unwrap(); + graph + .add_edge(5, 1, 2, [("p1", Prop::U64(5u64))], None) + .unwrap(); + graph + .add_edge(10, 1, 2, [("p1", Prop::U64(10u64))], None) + .unwrap(); + graph + } + + let filter = EdgeFilter.property("p1").temporal().first().eq(2u64); + + // No window; means the first update is at time 0 and the value of p1 is expected to be 1u64. + let expected_empty = []; + let expected_found = ["1->2"]; + + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_empty, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_empty, + TestVariants::PersistentOnly, + ); + + // Window(1,10); Expected emtpy because the first update is at time 0 and the value of p1 is expected to be 1u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(1..10), + filter.clone(), + &expected_empty, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(1..10), + filter.clone(), + &expected_empty, + TestVariants::PersistentOnly, + ); + + // Window(2,10); Expected update at time 2 and the value of p1 is expected to be 2u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(2..10), + filter.clone(), + &expected_found, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(2..10), + filter.clone(), + &expected_found, + TestVariants::PersistentOnly, + ); + + // Window(3,10); Expected update at time 2 (even if it is outside the window) and the value of p1 is expected to be 2u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(3..10), + filter.clone(), + &expected_found, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(3..10), + filter.clone(), + &expected_found, + TestVariants::PersistentOnly, + ); + + // Window(4,10); Expected update at time 2 (even if it is outside the window) and the value of p1 is expected to be 2u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(4..10), + filter.clone(), + &expected_found, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(4..10), + filter.clone(), + &expected_found, + TestVariants::PersistentOnly, + ); + + // Window(5,10); Expected update at time 5 (even if it is outside the window) and the value of p1 is expected to be 5u64. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(5..10), + filter.clone(), + &expected_empty, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(5..10), + filter.clone(), + &expected_empty, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_metadata_semantics() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.metadata("p1").eq(1u64); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", "N15->N1", + "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_metadata_semantics2() { + fn filter_edges(graph: &Graph, filter: impl CreateFilter) -> Vec { + let mut results = graph + .filter(filter) + .unwrap() + .edges() + .iter() + .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) + .collect::>(); + results.sort(); + results + } + + let graph = init_graph(Graph::new()); + + let filter = EdgeFilter.metadata("p1").eq(1u64); + assert_eq!( + filter_edges(&graph, filter.clone()), + vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", + "N15->N1", "N9->N10" + ] + ); + + let edge = graph + .add_edge(1, "shivam", "kapoor", [("p1", 100u64)], Some("fire_nation")) + .unwrap(); + edge.add_metadata([("z", true)], Some("fire_nation")) + .unwrap(); + let prop = graph.edge("shivam", "kapoor").unwrap().metadata().get("z"); + assert_eq!(prop, Some(Prop::map([("fire_nation", true)]))); + + let filter2 = EdgeFilter + .metadata("z") + .eq(Prop::map([("fire_nation", true)])); + assert_eq!(filter_edges(&graph, filter2), vec!["shivam->kapoor"]); + + let filter = EdgeFilter + .metadata("p1") + .eq(Prop::map([("_default", 1u64)])); + assert_eq!( + filter_edges(&graph, filter), + vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", + "N15->N1", "N9->N10" + ] + ); + } + + #[test] + fn test_temporal_any_semantics() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").temporal().any().eq(1u64); + let expected_results = vec![ + "N1->N2", "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N9", + ]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_temporal_any_semantics_for_event_ids() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").temporal().any().lt(2u64); + let expected_results = vec![ + "N1->N2", "N16->N15", "N17->N16", "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", + "N7->N8", "N8->N9", + ]; + assert_filter_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_temporal_latest_semantics() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").temporal().last().eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_temporal_latest_semantics_for_event_ids() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").temporal().last().eq(1u64); + let expected_results = + vec!["N1->N2", "N16->N15", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_property_semantics() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p1").ge(2u64); + let expected_results = vec![ + "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", + "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_property_semantics_for_event_ids() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = + vec!["N1->N2", "N16->N15", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_graph_for_event_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_property_semantics_only_metadata() { + // For this graph there won't be any temporal property index for property name "p1". + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + (2, "N1", "N2", vec![("q1", Prop::U64(0u64))]), + (2, "N2", "N3", vec![]), + ]; + + for (time, src, dst, props) in edges { + graph.add_edge(time, src, dst, props, None).unwrap(); + } + + let metadata_edges = [ + ("N1", "N2", vec![("p1", Prop::U64(1u64))]), + ("N2", "N3", vec![("p1", Prop::U64(1u64))]), + ]; + + for (src, dst, props) in metadata_edges { + graph + .edge(src, dst) + .unwrap() + .add_metadata(props.clone(), None) + .unwrap(); + } + + graph + } + + let filter = EdgeFilter.property("p1").eq(1u64); + let graph = init_graph(Graph::new()); + assert!(matches!( + graph.filter(filter.clone()).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "p1" + )); + assert!(matches!( + graph.persistent_graph().filter(filter).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "p1" + )); + } + + #[test] + fn test_property_semantics_only_temporal() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + // For this graph there won't be any metadata index for property name "p1". + fn init_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let edges = [ + (1, "N1", "N2", vec![("p1", Prop::U64(1u64))]), + (2, "N2", "N3", vec![("p1", Prop::U64(1u64))]), + (3, "N2", "N3", vec![("p1", Prop::U64(2u64))]), + (2, "N3", "N4", vec![("p1", Prop::U64(2u64))]), + (3, "N3", "N4", vec![("p1", Prop::U64(1u64))]), + (2, "N4", "N5", vec![]), + ]; + + for (time, src, dst, props) in edges { + graph.add_edge(time, src, dst, props, None).unwrap(); + } + + graph + } + + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4"]; + assert_filter_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + } +} + +fn init_nodes_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, +>( + graph: G, +) -> G { + let nodes = [ + ( + 1, + "1", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 5u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "2", + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_boat".into_prop()), + ("p40", 10u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "2", + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 15u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 4, + "2", + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 20u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "1", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 10u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 3, + "3", + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + Some("fire_nation"), + ), + ( + 4, + "1", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 15u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 3, + "4", + vec![ + ("p4", "pometry".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + None, + ), + ( + 4, + "4", + vec![ + ("p5", 12u64.into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_ship".into_prop()), + ], + None, + ), + ]; + + for (time, id, props, node_type) in nodes { + graph.add_node(time, id, props, node_type, None).unwrap(); + } + + let metadata = [ + ( + "1", + vec![ + ("m1", "pometry".into_prop()), + ("m2", "raphtory".into_prop()), + ], + ), + ("2", vec![("m1", "raphtory".into_prop())]), + ( + "3", + vec![ + ("m2", "pometry".into_prop()), + ("m3", "raphtory".into_prop()), + ], + ), + ( + "4", + vec![ + ("m3", "pometry".into_prop()), + ("m4", "raphtory".into_prop()), + ], + ), + ]; + + for (node_id, md) in metadata { + graph.node(node_id).unwrap().add_metadata(md).unwrap(); + } + + graph +} + +fn init_nodes_graph_with_num_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, +>( + graph: G, +) -> G { + let nodes = [ + ( + 1, + 1, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 5u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + 2, + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_boat".into_prop()), + ("p40", 10u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + 2, + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 15u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 4, + 2, + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 20u64.into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + 1, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 10u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 3, + 3, + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + Some("fire_nation"), + ), + ( + 4, + 1, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p9", 5u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ("p40", 15u64.into_prop()), + ], + Some("fire_nation"), + ), + ( + 3, + 4, + vec![ + ("p4", "pometry".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + None, + ), + ( + 4, + 4, + vec![ + ("p5", 12u64.into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_ship".into_prop()), + ], + None, + ), + ]; + + for (time, id, props, node_type) in nodes { + graph.add_node(time, id, props, node_type, None).unwrap(); + } + + graph +} + +fn init_nodes_graph_with_str_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, +>( + graph: G, +) -> G { + let nodes = [ + (1, "London", Some("fire_nation")), + (2, "Two", Some("air_nomads")), + (3, "Two", Some("air_nomads")), + (4, "Two", Some("air_nomads")), + (3, "London", Some("fire_nation")), + (3, "Tokyo", Some("fire_nation")), + (4, "London", Some("fire_nation")), + (3, "France Paris", None), + (4, "France Paris", None), + ]; + + for (time, id, node_type) in nodes { + graph.add_node(time, id, NO_PROPS, node_type, None).unwrap(); + } + + graph +} + +fn init_edges_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, +>( + graph: G, +) -> G { + let edges = [ + ( + 1, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 4u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "2", + "3", + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "2", + "3", + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "3", + "1", + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + Some("fire_nation"), + ), + ( + 3, + "2", + "1", + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + None, + ), + ( + 4, + "David Gilmour", + "John Mayer", + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + None, + ), + ( + 4, + "John Mayer", + "Jimmy Page", + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + None, + ), + ]; + + for (time, src, dst, props, edge_type) in edges { + graph.add_edge(time, src, dst, props, edge_type).unwrap(); + } + + graph +} + +fn init_edges_graph2< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, +>( + graph: G, +) -> G { + let edges = [ + ( + 1, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 6u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 7u64.into_prop()), + ("p10", "Gold_ship".into_prop()), + ("p20", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + "1", + "2", + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 4u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ], + Some("air_nomads"), + ), + ( + 2, + "2", + "3", + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "2", + "3", + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + "3", + "1", + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + Some("air_nomads"), + ), + ( + 3, + "2", + "1", + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + None, + ), + ]; + + for (time, src, dst, props, edge_type) in edges { + graph.add_edge(time, src, dst, props, edge_type).unwrap(); + } + + graph +} + +fn init_edges_graph_with_num_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, +>( + graph: G, +) -> G { + let edges = [ + ( + 1, + 1, + 2, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p10", "Paper_airplane".into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + 1, + 2, + vec![ + ("p1", "shivam_kapoor".into_prop()), + ("p2", 4u64.into_prop()), + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_ship".into_prop()), + ], + Some("fire_nation"), + ), + ( + 2, + 2, + 3, + vec![ + ("p1", "prop12".into_prop()), + ("p2", 2u64.into_prop()), + ("p10", "Paper_ship".into_prop()), + ("p20", "Gold_boat".into_prop()), + ("p30", "Old_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + 2, + 3, + vec![ + ("p20", "Gold_ship".into_prop()), + ("p30", "Gold_boat".into_prop()), + ], + Some("air_nomads"), + ), + ( + 3, + 3, + 1, + vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], + Some("fire_nation"), + ), + ( + 3, + 2, + 1, + vec![ + ("p2", 6u64.into_prop()), + ("p3", 1u64.into_prop()), + ("p10", "Paper_airplane".into_prop()), + ], + None, + ), + ]; + + for (time, src, dst, props, edge_type) in edges { + graph.add_edge(time, src, dst, props, edge_type).unwrap(); + } + + graph +} + +fn init_edges_graph_with_str_ids< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, +>( + graph: G, +) -> G { + let edges = [ + (1, "London", "Paris", Some("fire_nation")), + (2, "London", "Paris", Some("fire_nation")), + (2, "Two", "Three", Some("air_nomads")), + (3, "Two", "Three", Some("air_nomads")), + (3, "Three", "One", Some("fire_nation")), + (3, "Two", "One", None), + (4, "David Gilmour", "John Mayer", None), + (4, "John Mayer", "Jimmy Page", None), + ]; + + for (time, src, dst, edge_type) in edges { + graph.add_edge(time, src, dst, NO_PROPS, edge_type).unwrap(); + } + + graph +} + +fn init_edges_graph_with_str_ids_del< + G: StaticGraphViewOps + + AdditionOps + + DeletionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, +>( + graph: G, +) -> G { + let edges = [ + (1, "London", "Paris", Some("fire_nation")), + (2, "London", "Paris", Some("fire_nation")), + (2, "Two", "Three", Some("air_nomads")), + (3, "Two", "Three", Some("air_nomads")), + (3, "Three", "One", Some("fire_nation")), + (3, "Two", "One", None), + (4, "David Gilmour", "John Mayer", None), + (4, "John Mayer", "Jimmy Page", None), + ]; + + for (time, src, dst, edge_type) in edges { + graph.add_edge(time, src, dst, NO_PROPS, edge_type).unwrap(); + } + + graph + .delete_edge(3, "London", "Paris", Some("fire_nation")) + .unwrap(); + + graph + .add_edge(5, "Bangalore", "Bangalore", NO_PROPS, None) + .unwrap(); + + graph +} + +mod test_node_filter { + use crate::{ + init_nodes_graph, init_nodes_graph_with_num_ids, init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + }; + use raphtory::{ + algorithms::alternating_mask::alternating_mask, + db::{ + api::view::{filter_ops::NodeSelect, Filter}, + graph::views::filter::model::{ + node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, + ComposableFilter, CompositeNodeFilter, NodeViewFilterOps, TryAsCompositeFilter, + ViewWrapOps, + }, + }, + prelude::{ + AdditionOps, Graph, GraphViewOps, NodeFilter, NodeStateOps, NodeViewOps, TimeOps, + NO_PROPS, + }, + }; + use raphtory_test_utils::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, assert_select_nodes_results, + TestVariants, + }; + + #[test] + fn test_node_list_is_preserved() { + let graph = init_nodes_graph(Graph::new()); + let nodes = graph + .nodes() + .after(5) + .select(NodeFilter::node_type().contains("x")) + .unwrap(); + let degrees = nodes.degree(); + let degrees_collected = degrees.compute(); + assert_eq!(degrees, degrees_collected); + } + + #[test] + fn test_filter_nodes_for_node_name_eq() { + let filter = NodeFilter::name().eq("3"); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_name_ne() { + let filter = NodeFilter::name().ne("2"); + let expected_results = vec!["1", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_name_in() { + let filter = NodeFilter::name().is_in(vec!["1"]); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::name().is_in(vec![""]); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::name().is_in(vec!["2", "3"]); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_name_not_in() { + let filter = NodeFilter::name().is_not_in(vec!["1"]); + let expected_results = vec!["2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::name().is_not_in(vec![""]); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_type_eq() { + let filter = NodeFilter::node_type().eq("fire_nation"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_type_ne() { + let filter = NodeFilter::node_type().ne("fire_nation"); + let expected_results = vec!["2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_type_in() { + let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomads"]); + let expected_results = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_type_not_in() { + let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); + let expected_results = vec!["2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_type_starts_with() { + let filter = NodeFilter::node_type().starts_with("fire"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::node_type().starts_with("rocket"); + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_type_ends_with() { + let filter = NodeFilter::node_type().ends_with("nomads"); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::node_type().ends_with("circle"); + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_type_contains() { + let filter = NodeFilter::node_type().contains("fire"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_node_type_contains_not() { + let filter = NodeFilter::node_type().not_contains("fire"); + let expected_results = vec!["2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_fuzzy_search() { + let filter = NodeFilter::node_type().fuzzy_search("fire", 2, true); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::node_type().fuzzy_search("fire", 2, false); + let expected_results: Vec<&str> = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::node_type().fuzzy_search("air_noma", 2, false); + let expected_results: Vec<&str> = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_not_node_type() { + let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]).not(); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_eq_node_id() { + let filter = NodeFilter::id().eq("1"); + let expected_results = vec!["1"]; + + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::id().eq(1); + let expected_results = vec!["1"]; + + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_ne_node_id() { + let filter = NodeFilter::id().ne("1"); + let expected_results = vec!["2", "3", "4"]; + + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::id().ne(1); + let expected_results = vec!["2", "3", "4"]; + + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_is_in_node_id() { + let filter = NodeFilter::id().is_in(vec!["1", "3", "6"]); + let expected_results = vec!["1", "3"]; + + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::id().is_in(vec![1, 3, 6]); + let expected_results = vec!["1", "3"]; + + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_is_not_in_node_id() { + let filter = NodeFilter::id().is_not_in(vec!["1", "3", "6"]); + let expected_results = vec!["2", "4"]; + + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::id().is_not_in(vec![1, 3, 6]); + let expected_results = vec!["2", "4"]; + + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_lt_node_id() { + let filter = NodeFilter::id().lt(2); + let expected_results = vec!["1"]; + + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_le_node_id() { + let filter = NodeFilter::id().le(3); + let expected_results = vec!["1", "2", "3"]; + + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_gt_node_id() { + let filter = NodeFilter::id().gt(2); + let expected_results = vec!["3", "4"]; + + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_ge_node_id() { + let filter = NodeFilter::id().ge(2); + let expected_results = vec!["2", "3", "4"]; + + assert_filter_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_starts_with_node_id() { + let filter = NodeFilter::id().starts_with("France"); + let expected_results = vec!["France Paris"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_ends_with_node_id() { + let filter = NodeFilter::id().ends_with("wo"); + let expected_results = vec!["Two"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_contains_node_id() { + let filter = NodeFilter::id().contains("o"); + let expected_results = vec!["London", "Tokyo", "Two"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_not_contains_node_id() { + let filter = NodeFilter::id().not_contains("o"); + let expected_results = vec!["France Paris"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_is_in_node_id_str() { + let filter = NodeFilter::id().is_in(vec!["London", "Tokyo"]); + let expected_results = vec!["London", "Tokyo"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_is_not_in_node_id_str() { + let filter = NodeFilter::id().is_not_in(vec!["London", "Tokyo"]); + let expected_results = vec!["France Paris", "Two"]; + assert_filter_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_is_active_node_window() { + let filter = NodeFilter.window(1, 10).is_active(); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_is_active_node_window_not() { + let filter = NodeFilter + .window(1, 10) + .is_active() + .try_as_composite_node_filter() + .unwrap(); + let filter = CompositeNodeFilter::Not(Box::new(filter)); + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_is_active_node_latest() { + let filter = NodeFilter.latest().is_active(); + let expected_results = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_filter_by_column() { + let graph = Graph::new(); + graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 3, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 5, NO_PROPS, None, None).unwrap(); + + let mask = alternating_mask(&graph); + let expected_nodes: Vec<_> = graph + .nodes() + .name() + .iter_values() + .skip(1) + .step_by(2) + .collect(); + + let filtered = graph + .filter(NodeFilter::by_column(&mask, "bool_col").unwrap()) + .unwrap(); + + let names = filtered + .nodes() + .iter() + .map(|n| n.id().to_string()) + .collect::>(); + + assert_eq!(names, expected_nodes); + + let filtered = graph + .nodes() + .select(NodeFilter::by_column(&mask, "bool_col").unwrap()) + .unwrap(); + + let names = filtered + .iter() + .map(|n| n.id().to_string()) + .collect::>(); + + assert_eq!(names, expected_nodes); + } + + #[test] + fn test_is_active_node_snapshot_at() { + let filter = NodeFilter.snapshot_at(2).is_active(); + let expected_results = vec!["2"]; + assert_select_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } +} + +#[cfg(test)] +mod test_node_property_filter { + use crate::{init_nodes_graph, IdentityGraphTransformer}; + use raphtory::db::graph::views::filter::model::{ + graph_filter::GraphFilter, + node_filter::NodeFilter, + not_filter::NotFilter, + property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, + windowed_filter::Windowed, + ComposableFilter, PropertyFilterFactory, TemporalPropertyFilterFactory, ViewWrapOps, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_test_utils::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestVariants, + }; + use std::vec; + + #[test] + fn test_exact_match() { + // let filter = NodeFilter.degree > 5 + let filter = NodeFilter.property("p10").eq("Paper_airplane"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p10").eq(""); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_not_exact_match() { + let filter = NodeFilter.property("p10").eq("Paper"); + let expected_results: Vec<&str> = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_eq() { + let filter = NodeFilter.property("p2").eq(2u64); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p30").temporal().first().eq("Old_boat"); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p20").temporal().all().eq("Gold_ship"); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_ne() { + let filter = NodeFilter.property("p2").ne(2u64); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p30").temporal().first().ne("Old_boat"); + let expected_results = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p1").temporal().all().ne("Gold_ship"); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_lt() { + let filter = NodeFilter.property("p2").lt(10u64); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").temporal().first().lt(10u64); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p9").temporal().all().lt(10u64); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_le() { + let filter = NodeFilter.property("p2").le(6u64); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p9").temporal().first().le(10u64); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p2").temporal().all().le(10u64); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_gt() { + let filter = NodeFilter.property("p2").gt(2u64); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").temporal().first().gt(5u64); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p9").temporal().all().gt(1u64); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_ge() { + let filter = NodeFilter.property("p2").ge(2u64); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").temporal().first().ge(5u64); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").temporal().all().ge(5u64); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_in() { + let filter = NodeFilter.property("p2").is_in(vec![Prop::U64(6)]); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p2") + .is_in(vec![Prop::U64(2), Prop::U64(6)]); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p40") + .temporal() + .first() + .is_in(vec![Prop::U64(5)]); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p2") + .temporal() + .any() + .is_in(vec![Prop::U64(2)]); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_not_in() { + let filter = NodeFilter.property("p2").is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p2") + .temporal() + .all() + .is_not_in(vec![Prop::U64(2)]); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_is_some() { + let filter = NodeFilter.property("p2").is_some(); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").is_some(); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_is_none() { + let filter = NodeFilter.property("p2").is_none(); + let expected_results = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p40").is_none(); + let expected_results = vec!["3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_starts_with() { + let filter = NodeFilter.property("p10").starts_with("Pa"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .any() + .starts_with("Pap"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .starts_with("Pape"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .starts_with("Yohan"); + let expected_results: Vec<&str> = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p30") + .temporal() + .first() + .starts_with("Gold"); + let expected_results: Vec<&str> = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p20") + .temporal() + .all() + .starts_with("Gold"); + let expected_results: Vec<&str> = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_ends_with() { + let filter = NodeFilter.property("p10").ends_with("lane"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .any() + .ends_with("ship"); + let expected_results: Vec<&str> = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .ends_with("ane"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .ends_with("Jerry"); + let expected_results: Vec<&str> = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p20") + .temporal() + .first() + .ends_with("boat"); + let expected_results: Vec<&str> = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p20") + .temporal() + .all() + .ends_with("ship"); + let expected_results: Vec<&str> = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_contains() { + let filter = NodeFilter.property("p10").contains("Paper"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .any() + .contains("Paper"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .contains("Paper"); + let expected_results: Vec<&str> = vec!["1", "2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p30") + .temporal() + .first() + .contains("Old"); + let expected_results: Vec<&str> = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p30").temporal().all().contains("Gold"); + let expected_results: Vec<&str> = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_property_contains_not() { + let filter = NodeFilter.property("p10").not_contains("ship"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .any() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p10") + .temporal() + .last() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p30") + .temporal() + .first() + .not_contains("Old"); + let expected_results: Vec<&str> = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p30") + .temporal() + .all() + .not_contains("boat"); + let expected_results: Vec<&str> = vec!["1", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_not_property() { + let filter = NotFilter(NodeFilter.property("p10").contains("Paper")); + let expected_results: Vec<&str> = vec!["4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p10").contains("Paper").not(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_sum() { + let filter = NodeFilter.property("p9").temporal().sum().eq(15u64); + let expected_results: Vec<&str> = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_avg() { + let filter = NodeFilter.property("p2").temporal().avg().le(10f64); + let expected_results: Vec<&str> = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_min() { + let filter = NodeFilter.property("p40").temporal().min().is_in(vec![ + Prop::U64(5), + Prop::U64(10), + Prop::U64(20), + ]); + let expected_results: Vec<&str> = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_max() { + let filter = NodeFilter.property("p3").temporal().max().is_not_in(vec![ + Prop::U64(5), + Prop::U64(10), + Prop::U64(20), + ]); + let expected_results: Vec<&str> = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_nodes_for_temporal_property_len() { + let filter = NodeFilter.property("p2").temporal().len().le(5u64); + let expected_results: Vec<&str> = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_window_filter() { + let filter = NodeFilter + .window(1, 3) + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + // Wider window includes node 3 + let filter = NodeFilter + .window(1, 5) + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_window_filter_on_non_temporal_property() { + let filter1 = NodeFilter.window(1, 2).property("p1").eq("shivam_kapoor"); + let filter2 = NodeFilter + .window(100, 200) + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter1.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter1.clone(), + &expected_results, + TestVariants::All, + ); + + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter + .window(100, 200) + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_window_filter_any_all_over_window() { + let filter = NodeFilter + .window(3, 5) + .property("p20") + .temporal() + .any() + .eq("Gold_boat"); + + let expected_results = vec!["4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .window(3, 5) + .property("p20") + .temporal() + .all() + .eq("Gold_boat"); + + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_window_filter_and() { + // Filters both node 1 and 3 + let filter1 = NodeFilter + .window(1, 4) + .property("p10") + .temporal() + .any() + .eq("Paper_airplane"); + + // Filters only node 3 + let filter2 = NodeFilter + .window(3, 6) + .property("p2") + .temporal() + .sum() + .eq(6u64); + + let filter = filter1.and(filter2); + + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_at_filter() { + // Only time=2 contributes; node 2 has p2=2 at t=2 + let filter = NodeFilter.at(2).property("p2").temporal().sum().eq(2u64); + + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + // Only time=3 contributes; node 3 has p2=6 at t=3 + let filter = NodeFilter.at(3).property("p2").temporal().sum().eq(6u64); + + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_after_filter() { + // after(2) means t >= 3 + let filter = NodeFilter.after(2).property("p2").temporal().sum().ge(6u64); + + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_before_filter() { + // before(3) means t <= 2 + let filter = NodeFilter + .before(3) + .property("p2") + .temporal() + .sum() + .eq(2u64); + + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + // And node 3 shouldn't match, because its p2=6 lives at t=3. + let filter = NodeFilter + .before(3) + .property("p2") + .temporal() + .sum() + .eq(6u64); + + let expected_results = vec![]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_latest_filter() { + // At latest time (currently t=4), only node 4 has p5=12 + let filter = NodeFilter.latest().property("p5").eq(12u64); + + let expected_results = vec!["4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_snapshot_at_semantics_event_graph() { + let t = 2; + + let filter_snapshot = NodeFilter.snapshot_at(t).property("p2").eq(2u64); + + let filter_before = NodeFilter.before(t + 1).property("p2").eq(2u64); + + let expected_results = vec!["2"]; + + // snapshot_at + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot, + &expected_results, + TestVariants::EventOnly, + ); + + // before(t+1) + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_before.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_before, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_snapshot_at_semantics_persistent_graph() { + let t = 2; + + let filter_snapshot = NodeFilter.snapshot_at(t).property("p2").eq(2u64); + + let filter_at = NodeFilter.at(t).property("p2").eq(2u64); + + let expected_results = vec!["2"]; + + // snapshot_at + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot, + &expected_results, + TestVariants::PersistentOnly, + ); + + // at(t) + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_at.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_at, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_snapshot_latest_semantics_event_graph() { + let filter_snapshot_latest = NodeFilter + .snapshot_latest() + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let filter_noop = NodeFilter.property("p2").temporal().sum().ge(2u64); + + // From your earlier window test, "2" and "3" are the ones with p2 values across time. + // If your underlying dataset changes, adjust this accordingly. + let expected_results = vec!["2", "3"]; + + // snapshot_latest + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot_latest.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot_latest, + &expected_results, + TestVariants::EventOnly, + ); + + // no-op baseline + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_noop.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_noop, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_snapshot_latest_semantics_persistent_graph() { + let filter_snapshot_latest = NodeFilter + .snapshot_latest() + .property("p1") + .eq("shivam_kapoor"); + + let filter_latest = NodeFilter.latest().property("p1").eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + + // snapshot_latest + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot_latest.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_snapshot_latest, + &expected_results, + TestVariants::PersistentOnly, + ); + + // latest + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_latest.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter_latest, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_nodes_layer_filter() { + let filter = NodeFilter + .layer("_default") + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_nodes_layer_then_window_ordering() { + // In layer "fire_nation" within window [1,4), node "1" matches p1 == "shivam_kapoor". + let filter = NodeFilter + .layer("fire_nation") + .window(1, 4) + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_nodes_window_then_layer_ordering() { + // Same semantics as above, but reversed chaining order. + let filter = NodeFilter + .window(1, 4) + .layer("fire_nation") + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1"]; + + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_window() { + let filter: Windowed = GraphFilter.window(1, 2); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.window(1, 3); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.window(4, 6); + let expected_results = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = GraphFilter.window(4, 6); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_graph_filter_layer() { + let filter = GraphFilter.layer("fire_nation"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.layer("air_nomads"); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_graph_filter_window_then_layer() { + let filter = GraphFilter.window(1, 3).layer("fire_nation"); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.window(4, 4).layer("air_nomads"); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + #[ignore] // TODO: Enable this when node layer is supported + fn test_graph_filter_layer_then_window() { + let filter = GraphFilter.layer("fire_nation").window(1, 3); + let expected_results = vec!["1", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_at() { + let filter = GraphFilter.at(1); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.at(2); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = GraphFilter.at(2); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = GraphFilter.at(3); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_after() { + let filter = GraphFilter.after(3); + let expected_results = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = GraphFilter.after(3); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_graph_filter_before() { + let filter = GraphFilter.before(2); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.before(3); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_snapshot_at() { + let filter = GraphFilter.snapshot_at(1); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.snapshot_at(3); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = GraphFilter.snapshot_at(4); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_snapshot_latest() { + let filter = GraphFilter.snapshot_latest(); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_graph_filter_latest() { + let filter = GraphFilter.latest(); + let expected_results = vec!["1", "2", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = GraphFilter.latest(); + let expected_results = vec!["1", "2", "3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } +} + +#[cfg(test)] +mod test_node_composite_filter { + use raphtory_api::core::Direction; + + use crate::{init_edges_graph, init_nodes_graph, IdentityGraphTransformer}; + use raphtory::{ + db::graph::views::filter::model::{ + node_filter::ops::NodeFilterOps, property_filter::ops::PropertyFilterOps, + ComposableFilter, PropertyFilterFactory, TryAsCompositeFilter, + }, + prelude::NodeFilter, + }; + use raphtory_test_utils::assertions::{ + assert_filter_neighbours_results, assert_filter_nodes_results, assert_search_nodes_results, + TestVariants, + }; + + #[test] + fn test_filter_nodes_by_props_added_at_different_times() { + let filter = NodeFilter + .property("p4") + .eq("pometry") + .and(NodeFilter.property("p5").eq(12u64)); + let expected_results = vec!["4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_unique_results_from_composite_filters() { + let filter = NodeFilter + .property("p2") + .ge(2u64) + .and(NodeFilter.property("p2").ge(1u64)); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p2") + .ge(2u64) + .or(NodeFilter.property("p2").ge(5u64)); + let expected_results = vec!["2", "3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_composite_filter_nodes() { + let filter = NodeFilter + .property("p2") + .eq(2u64) + .and(NodeFilter.property("p1").eq("kapoor")); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p2") + .eq(2u64) + .or(NodeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter.property("p1").eq("pometry").or(NodeFilter + .property("p2") + .eq(6u64) + .and(NodeFilter.property("p3").eq(1u64))); + let expected_results = vec!["3"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::node_type() + .eq("fire_nation") + .and(NodeFilter.property("p1").eq("prop1")); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter + .property("p9") + .eq(5u64) + .and(NodeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::node_type() + .eq("fire_nation") + .and(NodeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::name() + .eq("2") + .and(NodeFilter.property("p2").eq(2u64)); + let expected_results = vec!["2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::name() + .eq("2") + .and(NodeFilter.property("p2").eq(2u64)) + .or(NodeFilter.property("p9").eq(5u64)); + let expected_results = vec!["1", "2"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_node_filter().unwrap(); + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_not_composite_filter_nodes() { + let filter = NodeFilter::name() + .eq("2") + .and(NodeFilter.property("p2").eq(2u64)) + .or(NodeFilter.property("p9").eq(5u64)) + .not(); + let expected_results = vec!["3", "4"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = NodeFilter::name() + .eq("2") + .not() + .and(NodeFilter.property("p2").eq(2u64)) + .or(NodeFilter.property("p9").eq(5u64)); + let expected_results = vec!["1"]; + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_out_neighbours_filter() { + let filter = NodeFilter::name() + .eq("2") + .and(NodeFilter.property("p2").eq(2u64)); + let expected_results = vec!["2"]; + assert_filter_neighbours_results( + |graph| init_edges_graph(init_nodes_graph(graph)), + IdentityGraphTransformer, + "1", + Direction::OUT, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_in_neighbours_filter() { + let filter = NodeFilter.property("p9").ge(1u64); + let expected_results = vec!["1"]; + assert_filter_neighbours_results( + |graph| init_edges_graph(init_nodes_graph(graph)), + IdentityGraphTransformer, + "2", + Direction::IN, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_neighbours_filter() { + let filter = NodeFilter.property("p10").contains("Paper"); + let expected_results = vec!["1", "3"]; + assert_filter_neighbours_results( + |graph| init_edges_graph(init_nodes_graph(graph)), + IdentityGraphTransformer, + "2", + Direction::BOTH, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } +} + +#[cfg(test)] +mod test_node_property_filter_agg { + use crate::IdentityGraphTransformer; + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::{ + model::{ + node_filter::NodeFilter, + property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, + PropertyFilterFactory, TemporalPropertyFilterFactory, TryAsCompositeFilter, + }, + CreateFilter, + }, + }, + prelude::{AdditionOps, GraphViewOps, PropertyAdditionOps}, + }; + use raphtory_api::core::{ + entities::properties::prop::{IntoProp, Prop}, + storage::arc_str::ArcStr, + }; + use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, + }; + use raphtory_test_utils::assertions::{ + assert_filter_nodes_err, assert_filter_nodes_results, assert_search_nodes_results, + TestVariants::All, + }; + + fn list_u8(xs: &[u8]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::U8)) + } + fn list_u16(xs: &[u16]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::U16)) + } + fn list_u32(xs: &[u32]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::U32)) + } + fn list_u64(xs: &[u64]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::U64)) + } + fn list_i32(xs: &[i32]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::I32)) + } + fn list_i64(xs: &[i64]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::I64)) + } + fn list_f32(xs: &[f32]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::F32)) + } + fn list_f64(xs: &[f64]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::F64)) + } + fn list_str(xs: &[&str]) -> Prop { + Prop::list(xs.iter().map(|s| Prop::Str(ArcStr::from(*s)))) + } + fn list_bool(xs: &[bool]) -> Prop { + Prop::list(xs.iter().copied().map(Prop::Bool)) + } + + #[inline] + fn list(v: Vec) -> Prop { + Prop::List(v.into()) + } + + pub fn init_nodes_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes: [(i64, &str, Vec<(&str, Prop)>); 12] = [ + ( + 1, + "n1", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u8s_max", list_u8(&[u8::MAX, u8::MAX])), // min: u8::MAX, max: u8::MAX, sum: 510 + ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u16s_max", list_u16(&[u16::MAX, u16::MAX])), // min: u16::MAX, max: u16::MAX, sum: 131070 + ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u32s_max", list_u32(&[u32::MAX, u32::MAX])), // min: 1, max: 3, sum: 8589934590 + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u64s_max", list_u64(&[u64::MAX, u64::MAX])), // min: 1, max: 3, sum: OVERFLOW + ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i64s", list_i64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 + ( + "nested_list", + list(vec![ + list(vec![ + list(vec![ + list(vec![50.0.into_prop(), 40.0.into_prop()]), + list(vec![60.0.into_prop()]), + ]), + list(vec![list(vec![46.0.into_prop()])]), + ]), + list(vec![list(vec![list(vec![90.0.into_prop()])])]), + ]), + ), + ], + ), + ( + 2, + "n1", + vec![ + ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 + ("p_bools", list_bool(&[true, true])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_u16s", list_u16(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_u32s", list_u32(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_i32s", list_i32(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_i64s", list_i64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5, 4.5])), // min: 1.0, max: 4.5, sum: 11.0, avg: 2.75, len: 4 + ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 + ], + ), + ( + 1, + "n2", + vec![ + ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 + ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 + ("p_bools", list_bool(&[false, false])), + ], + ), + ( + 2, + "n2", + vec![ + ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 + ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 + ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 + ], + ), + ( + 1, + "n3", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 1, 4])), // min: 1, max: 4, sum: 6, avg: 2.0, len: 3 + ("p_u16s", list_u16(&[1, 0, 5])), // min: 0, max: 5, sum: 6, avg: 2.0, len: 3 + ("p_u32s", list_u32(&[2, 2, 2])), // min: 2, max: 2, sum: 6, avg: 2.0, len: 3 + ("p_u64s", list_u64(&[0, 3, 3])), // min: 0, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i32s", list_i32(&[-1, 4, 3])), // min: -1, max: 4, sum: 6, avg: 2.0, len: 3 + ("p_i64s", list_i64(&[0, 3, -3])), // min: -3, max: 3, sum: 0, avg: 0.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.5, 3.0])), // min: 1.0, max: 3.0, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_f64s", list_f64(&[30.0, 60.0])), // min: 30.0, max: 60.0, sum: 90.0, avg: 45.0, len: 2 + ( + "nested_list", + list(vec![ + list(vec![ + list(vec![ + list(vec![50.0.into_prop(), 40.0.into_prop()]), + list(vec![60.0.into_prop()]), + ]), + list(vec![list(vec![46.0.into_prop()])]), + ]), + list(vec![list(vec![list(vec![90.0.into_prop()])])]), + ]), + ), + ], + ), + ( + 2, + "n3", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i64s", list_i64(&[1, 2, -3])), // min: -3, max: 2, sum: 0, avg: 0.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 + ( + "nested_list", + list(vec![ + list(vec![ + list(vec![ + list(vec![50.0.into_prop(), 40.0.into_prop()]), + list(vec![60.0.into_prop()]), + ]), + list(vec![list(vec![46.0.into_prop()])]), + ]), + list(vec![list(vec![list(vec![90.0.into_prop()])])]), + ]), + ), + ], + ), + ( + 1, + "n4", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_bools_all", list_bool(&[true, true])), + ], + ), + ( + 2, + "n4", + vec![ + ("p_strs", list_str(&["x", "y", "z"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[false, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u64s", list_u64(&[10, 20, 30])), // min: 10, max: 30, sum: 60, avg: 20.0, len: 3 + ("p_i32s", list_i32(&[10, 20, 30])), // min: 10, max: 30, sum: 60, avg: 20.0, len: 3 + ("p_f32s", list_f32(&[10.0, 20.0, 30.0])), // min: 10.0, max: 30.0, sum: 60.0, avg: 20.0, len: 3 + ("p_bools_all", list_bool(&[true, true])), + ], + ), + ( + 2, + "n5", + vec![ + ("p_u64s", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 + ("p_u64s_max", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 + ("p_u64s_min", list_u64(&[u64::MIN, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 + ("p_i64s", list_i64(&[i64::MAX, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 + ("p_i64s_max", list_i64(&[i64::MAX, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 + ("p_i64s_min", list_i64(&[i64::MIN, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 + ], + ), + ( + 2, + "n6", + vec![ + ("p_i32s", list_i32(&[-2, 1, 3])), // min: -2, max: 3, sum: 2, avg: 0.6666666666666666, len: 3 + ], + ), + ( + 1, + "n7", + vec![ + ("p_u64s", list_u64(&[])), // min: None, max: None, sum: None, avg: None, len: 0 + ], + ), + ( + 2, + "n10", + vec![ + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 + ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_i64s", list_i64(&[1, 2, -3])), // min: -3, max: 2, sum: 0, avg: 0.0, len: 3 + ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 + ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 + ("p_bools_all", list_bool(&[true, true])), + ], + ), + ]; + + for (t, id, props) in nodes { + graph.add_node(t, id, props, None, None).unwrap(); + } + + let metadata: [(&str, Vec<(&str, Prop)>); 8] = [ + ( + "n1", + vec![ + ("p_u8s", list_u8(&[2, 9])), // min: 2, max: 9, sum: 11, avg: 5.5, len: 2 + ("p_u16s", list_u16(&[3, 5])), // min: 3, max: 5, sum: 8, avg: 4.0, len: 2 + ("p_u32s", list_u32(&[4, 9])), // min: 4, max: 9, sum: 13, avg: 6.5, len: 2 + ], + ), + ( + "n2", + vec![ + ("p_u64s", list_u64(&[2, 3, 7])), // min: 2, max: 7, sum: 12, avg: 4.0, len: 3 + ], + ), + ( + "n3", + vec![ + ("p_i32s", list_i32(&[10, 2, -3])), // min: -3, max: 10, sum: 9, avg: 3.0, len: 3 + ("p_i64s", list_i64(&[1, 12, 3, 4])), // min: 1, max: 12, sum: 20, avg: 5.0, len: 4 + ], + ), + ( + "n4", + vec![ + ("p_f32s", list_f32(&[1.5, 2.5])), // min: 1.5, max: 2.5, sum: 4.0, avg: 2.0, len: 2 + ("p_f64s", list_f64(&[0.5, 1.5])), // min: 0.5, max: 1.5, sum: 2.0, avg: 1.0, len: 2 + ], + ), + ( + "n5", + vec![ + ("p_strs", list_str(&["m1", "m2", "m3"])), // min: None, max: None, sum: None, avg: None, len: 3 + ], + ), + ( + "n6", + vec![ + ("p_u64s", list_u64(&[])), // min: None, max: None, sum: None, avg: None, len: 0 + ("p_strs", list_str(&["a", "a"])), + ], + ), + ( + "n7", + vec![ + ("p_u64s", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: ~9.22e18, len: 2 + ("p_strs", list_str(&["a"])), + ], + ), + ( + "n10", + vec![ + ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 + ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 + ], + ), + ]; + + for (node_id, md) in metadata { + graph.node(node_id).unwrap().add_metadata(md).unwrap(); + } + + graph + } + + fn apply_assertion( + filter: impl TryAsCompositeFilter + CreateFilter + Clone, + expected: &[&str], + ) { + assert_filter_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected, + All, + ); + + assert_search_nodes_results( + init_nodes_graph, + IdentityGraphTransformer, + filter, + &expected, + All, + ); + } + + fn apply_assertion_err( + filter: impl TryAsCompositeFilter + CreateFilter + Clone, + expected: &str, + ) { + assert_filter_nodes_err( + init_nodes_graph, + IdentityGraphTransformer, + filter.clone(), + &expected, + All, + ); + + // assert_search_nodes_err( + // init_nodes_graph, + // IdentityGraphTransformer, + // filter, + // expected, + // All, + // ); + } + + // ------ Property: SUM ---- + #[test] + fn test_node_property_sum_u8s() { + let filter = NodeFilter.property("p_u8s").sum().eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_sum_u16s() { + let filter = NodeFilter.property("p_u16s").sum().eq(Prop::U64(6)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_sum_u32s() { + let filter = NodeFilter.property("p_u32s").sum().eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_sum_u64s() { + let filter = NodeFilter.property("p_u64s").sum().eq(Prop::U64(6)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_sum_i32s() { + let filter = NodeFilter.property("p_i32s").sum().eq(Prop::I64(2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_sum_i64s() { + let filter = NodeFilter.property("p_i64s").sum().eq(Prop::I64(0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_sum_f32s() { + let filter = NodeFilter.property("p_f32s").sum().eq(Prop::F64(6.5)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_sum_f64s() { + let filter = NodeFilter.property("p_f64s").sum().eq(Prop::F64(120.0)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + // ------ Property: AVG ---- + #[test] + fn test_node_property_avg_u8s() { + let filter = NodeFilter.property("p_u8s").avg().eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_u16s() { + let filter = NodeFilter.property("p_u16s").avg().eq(Prop::F64(2.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_u32s() { + let filter = NodeFilter.property("p_u32s").avg().eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_u64s() { + let filter = NodeFilter.property("p_u64s").avg().eq(Prop::F64(2.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .avg() + .eq(Prop::F64(0.6666666666666666)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_i64s() { + let filter = NodeFilter.property("p_i64s").avg().eq(Prop::F64(0.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .avg() + .eq(Prop::F64(2.1666666666666665)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_avg_f64s() { + let filter = NodeFilter.property("p_f64s").avg().eq(Prop::F64(40.0)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + // ------ Property: LEN ------ + #[test] + fn test_node_property_len_u8s() { + let filter = NodeFilter.property("p_u8s").len().eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_u16s() { + let filter = NodeFilter.property("p_u16s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_u32s() { + let filter = NodeFilter.property("p_u32s").len().eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_u64s() { + let filter = NodeFilter.property("p_u64s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_i32s() { + let filter = NodeFilter.property("p_i32s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_i64s() { + let filter = NodeFilter.property("p_i64s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_f32s() { + let filter = NodeFilter.property("p_f32s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_f64s() { + let filter = NodeFilter.property("p_f64s").len().eq(Prop::U64(3)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_len_strs() { + let filter = NodeFilter.property("p_strs").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + // ------ Property: MIN ------ + #[test] + fn test_node_property_min_u8s() { + let filter = NodeFilter.property("p_u8s").min().eq(Prop::U8(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_u16s() { + let filter = NodeFilter.property("p_u16s").min().eq(Prop::U16(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_u32s() { + let filter = NodeFilter.property("p_u32s").min().eq(Prop::U32(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_u64s() { + let filter = NodeFilter.property("p_u64s").min().eq(Prop::U64(1)); + let expected = vec!["n1", "n10", "n2", "n3", "n5"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_i32s() { + let filter = NodeFilter.property("p_i32s").min().eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_i64s() { + let filter = NodeFilter.property("p_i64s").min().eq(Prop::I64(-3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_f32s() { + let filter = NodeFilter.property("p_f32s").min().eq(Prop::F32(10.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_min_f64s() { + let filter = NodeFilter.property("p_f64s").min().eq(Prop::F64(40.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Property: MAX ------ + #[test] + fn test_node_property_max_u8s() { + let filter = NodeFilter.property("p_u8s").max().eq(Prop::U8(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_u16s() { + let filter = NodeFilter.property("p_u16s").max().eq(Prop::U16(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_u32s() { + let filter = NodeFilter.property("p_u32s").max().eq(Prop::U32(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_u64s() { + let filter = NodeFilter.property("p_u64s").max().eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_i32s() { + let filter = NodeFilter.property("p_i32s").max().eq(Prop::I32(3)); + let expected = vec!["n10", "n3", "n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_i64s() { + let filter = NodeFilter.property("p_i64s").max().eq(Prop::I64(2)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_f32s() { + let filter = NodeFilter.property("p_f32s").max().eq(Prop::F32(30.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_max_f64s() { + let filter = NodeFilter.property("p_f64s").max().eq(Prop::F64(50.0)); + let expected = vec!["n1", "n10", "n2", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: SUM ------ + #[test] + fn test_node_property_metadata_sum_u8s() { + let filter = NodeFilter.metadata("p_u8s").sum().eq(Prop::U64(11)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_u16s() { + let filter = NodeFilter.metadata("p_u16s").sum().eq(Prop::U64(8)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_u32s() { + let filter = NodeFilter.metadata("p_u32s").sum().eq(Prop::U64(13)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_u64s() { + let filter = NodeFilter.metadata("p_u64s").sum().eq(Prop::U64(12)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_i32s() { + let filter = NodeFilter.metadata("p_i32s").sum().eq(Prop::I64(9)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_i64s() { + let filter = NodeFilter.metadata("p_i64s").sum().eq(Prop::I64(20)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_f32s() { + let filter = NodeFilter.metadata("p_f32s").sum().eq(Prop::F64(4.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_sum_f64s() { + let filter = NodeFilter.metadata("p_f64s").sum().eq(Prop::F64(2.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: AVG ------ + #[test] + fn test_node_property_metadata_avg_u8s() { + let filter = NodeFilter.metadata("p_u8s").avg().eq(Prop::F64(5.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_u16s() { + let filter = NodeFilter.metadata("p_u16s").avg().eq(Prop::F64(4.0)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_u32s() { + let filter = NodeFilter.metadata("p_u32s").avg().eq(Prop::F64(6.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_u64s() { + let filter = NodeFilter.metadata("p_u64s").avg().eq(Prop::F64(4.0)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_i32s() { + let filter = NodeFilter.metadata("p_i32s").avg().eq(Prop::F64(3.0)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_i64s() { + let filter = NodeFilter.metadata("p_i64s").avg().eq(Prop::F64(5.0)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_f32s() { + let filter = NodeFilter.metadata("p_f32s").avg().eq(Prop::F64(2.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_avg_f64s() { + let filter = NodeFilter.metadata("p_f64s").avg().eq(Prop::F64(1.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: MIN ------ + #[test] + fn test_node_property_metadata_min_u8s() { + let filter = NodeFilter.metadata("p_u8s").min().eq(Prop::U8(2)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_u16s() { + let filter = NodeFilter.metadata("p_u16s").min().eq(Prop::U16(3)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_u32s() { + let filter = NodeFilter.metadata("p_u32s").min().eq(Prop::U32(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_u64s() { + let filter = NodeFilter.metadata("p_u64s").min().eq(Prop::U64(2)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_i32s() { + let filter = NodeFilter.metadata("p_i32s").min().eq(Prop::I32(-3)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_i64s() { + let filter = NodeFilter.metadata("p_i64s").min().eq(Prop::I64(1)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_f32s() { + let filter = NodeFilter.metadata("p_f32s").min().eq(Prop::F32(1.5)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_min_f64s() { + let filter = NodeFilter.metadata("p_f64s").min().eq(Prop::F64(0.5)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: MAX ------ + #[test] + fn test_node_property_metadata_max_u8s() { + let filter = NodeFilter.metadata("p_u8s").max().eq(Prop::U8(9)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_u16s() { + let filter = NodeFilter.metadata("p_u16s").max().eq(Prop::U16(5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_u32s() { + let filter = NodeFilter.metadata("p_u32s").max().eq(Prop::U32(9)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_u64s() { + let filter = NodeFilter.metadata("p_u64s").max().eq(Prop::U64(7)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_i32s() { + let filter = NodeFilter.metadata("p_i32s").max().eq(Prop::I32(10)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_i64s() { + let filter = NodeFilter.metadata("p_i64s").max().eq(Prop::I64(12)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_f32s() { + let filter = NodeFilter.metadata("p_f32s").max().eq(Prop::F32(2.5)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_max_f64s() { + let filter = NodeFilter.metadata("p_f64s").max().eq(Prop::F64(1.5)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: Len ------ + #[test] + fn test_node_property_metadata_len_u8s() { + let filter = NodeFilter.metadata("p_u8s").len().eq(Prop::U64(2)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_len_u16s() { + let filter = NodeFilter.metadata("p_u16s").len().eq(Prop::U64(2)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_len_u32s() { + let filter = NodeFilter.metadata("p_u32s").len().eq(Prop::U64(2)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_len_u64s() { + let filter = NodeFilter.metadata("p_u64s").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_len_i32s() { + let filter = NodeFilter.metadata("p_i32s").len().eq(Prop::U64(3)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_len_i64s() { + let filter = NodeFilter.metadata("p_i64s").len().eq(Prop::U64(4)); + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_len_f32s() { + let filter = NodeFilter.metadata("p_f32s").len().eq(Prop::U64(2)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_len_f64s() { + let filter = NodeFilter.metadata("p_f64s").len().eq(Prop::U64(2)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_metadata_len_strs() { + let filter = NodeFilter.metadata("p_strs").len().eq(Prop::U64(3)); + let expected = vec!["n10", "n5"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal last: SUM ------ + #[test] + fn test_node_property_temporal_last_sum_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_sum_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_sum_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_sum_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .sum() + .eq(Prop::U64(60)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_sum_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .sum() + .eq(Prop::I64(60)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_sum_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .sum() + .eq(Prop::I64(0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_sum_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .sum() + .eq(Prop::F64(6.5)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_sum_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .sum() + .eq(Prop::F64(90.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal last: AVG ------ + #[test] + fn test_node_property_temporal_last_avg_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_avg_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_avg_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_avg_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .avg() + .eq(Prop::F64(20.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .avg() + .eq(Prop::F64(0.6666666666666666)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_avg_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .avg() + .eq(Prop::F64(0.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .avg() + .eq(Prop::F64(20.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_avg_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .avg() + .eq(Prop::F64(45.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal last: MIN ------ + #[test] + fn test_node_property_temporal_last_min_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .min() + .eq(Prop::U8(1)); + let expected = vec!["n1", "n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_min_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .min() + .eq(Prop::U16(1)); + let expected = vec!["n1", "n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_min_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .min() + .eq(Prop::U32(1)); + let expected = vec!["n1", "n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_min_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .min() + .eq(Prop::U64(10)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_min_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .min() + .eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_min_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .min() + .eq(Prop::I64(-3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_min_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .min() + .eq(Prop::F32(10.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_min_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .min() + .eq(Prop::F64(40.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal last: MAX ------ + #[test] + fn test_node_property_temporal_last_max_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .max() + .eq(Prop::U8(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_max_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .max() + .eq(Prop::U16(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_max_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .max() + .eq(Prop::U32(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_max_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .max() + .eq(Prop::U64(30)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_max_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .max() + .eq(Prop::I32(3)); + let expected = vec!["n3", "n6", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_max_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_max_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .max() + .eq(Prop::F32(3.5)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_max_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .max() + .eq(Prop::F64(50.0)); + let expected = vec!["n1", "n2", "n3", "n10"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal last: LEN ------ + #[test] + fn test_node_property_temporal_last_len_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_len_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_len_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_len_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n4", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_len_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n4", "n6", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_len_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_len_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n3", "n4", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_last_len_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .last() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal all: SUM ------ + #[test] + fn test_node_property_temporal_all_sum_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_sum_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_sum_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_sum_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_sum_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .sum() + .eq(Prop::I64(6)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_sum_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .sum() + .eq(Prop::I64(0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_sum_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .sum() + .eq(Prop::F64(6.5)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_sum_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .sum() + .eq(Prop::F64(90.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal all: AVG ------ + #[test] + fn test_node_property_temporal_all_avg_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_avg_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_avg_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_avg_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_avg_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .avg() + .eq(Prop::F64(0.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .avg() + .eq(Prop::F64(2.1666666666666665)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_avg_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .avg() + .eq(Prop::F64(45.0)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal all: MIN ------ + #[test] + fn test_node_property_temporal_all_min_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .min() + .eq(Prop::U8(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_min_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .min() + .eq(Prop::U16(1)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_min_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .min() + .eq(Prop::U32(1)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_min_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .min() + .eq(Prop::U64(1)); + let expected = vec!["n1", "n10", "n2", "n5"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_min_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .min() + .eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_min_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .min() + .eq(Prop::I64(-3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_min_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .min() + .eq(Prop::F32(1.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_min_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .min() + .eq(Prop::F64(30.0)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal all: MAX ------ + #[test] + fn test_node_property_temporal_all_max_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .max() + .eq(Prop::U8(3)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_max_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .max() + .eq(Prop::U16(3)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_max_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .max() + .eq(Prop::U32(3)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_max_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .max() + .eq(Prop::U64(4)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_max_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .max() + .eq(Prop::I32(3)); + let expected = vec!["n10", "n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_max_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_max_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .max() + .eq(Prop::F32(3.5)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_max_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .max() + .eq(Prop::F64(50.0)); + let expected = vec!["n1", "n10", "n2"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal all: LEN ------ + #[test] + fn test_node_property_temporal_all_len_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_len_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_len_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_len_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .all() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_len_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_len_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .all() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_len_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .all() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_all_len_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .all() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal first: SUM ------ + #[test] + fn test_node_property_temporal_first_sum_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_sum_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_sum_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_sum_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_sum_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .sum() + .eq(Prop::I64(6)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_sum_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .sum() + .eq(Prop::I64(0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_sum_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .sum() + .eq(Prop::F64(6.5)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_sum_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .sum() + .eq(Prop::F64(90.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal first: AVG ------ + #[test] + fn test_node_property_temporal_first_avg_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_avg_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_avg_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_avg_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_avg_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .avg() + .eq(Prop::F64(0.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .avg() + .eq(Prop::F64(2.1666666666666665)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_avg_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .avg() + .eq(Prop::F64(45.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal first: MIN ------ + #[test] + fn test_node_property_temporal_first_min_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .min() + .eq(Prop::U8(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_min_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .min() + .eq(Prop::U16(1)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_min_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .min() + .eq(Prop::U32(1)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_min_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .min() + .eq(Prop::U64(1)); + let expected = vec!["n1", "n10", "n2", "n4", "n5"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_min_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .min() + .eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_min_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .min() + .eq(Prop::I64(-3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_min_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .min() + .eq(Prop::F32(1.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_min_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .min() + .eq(Prop::F64(30.0)); + let expected = vec!["n2", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal first: MAX ------ + #[test] + fn test_node_property_temporal_first_max_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .max() + .eq(Prop::U8(3)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_max_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .max() + .eq(Prop::U16(3)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_max_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .max() + .eq(Prop::U32(3)); + let expected = vec!["n1", "n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_max_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .max() + .eq(Prop::U64(4)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_max_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .max() + .eq(Prop::I32(3)); + let expected = vec!["n1", "n10", "n4", "n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_max_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n10"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_max_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .max() + .eq(Prop::F32(3.5)); + let expected = vec!["n1", "n10", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_max_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .max() + .eq(Prop::F64(50.0)); + let expected = vec!["n1", "n10", "n2"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal first: LEN ------ + #[test] + fn test_node_property_temporal_first_len_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_len_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_len_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_len_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_len_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_len_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .first() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_len_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .first() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_first_len_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .first() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal any: SUM ------ + #[test] + fn test_node_property_temporal_any_sum_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_sum_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_sum_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_sum_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .sum() + .eq(Prop::U64(6)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .sum() + .eq(Prop::U64(10)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_sum_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .sum() + .eq(Prop::I64(6)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .sum() + .eq(Prop::I64(60)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_sum_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .sum() + .eq(Prop::I64(0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .sum() + .eq(Prop::I64(10)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_sum_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .sum() + .eq(Prop::F64(6.5)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .sum() + .eq(Prop::F64(60.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_sum_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .sum() + .eq(Prop::F64(90.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .sum() + .eq(Prop::F64(120.0)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal any: AVG ------ + #[test] + fn test_node_property_temporal_any_avg_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_avg_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_avg_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_avg_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_avg_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_avg_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(0.0)); + let expected = vec!["n3", "n10"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.5)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_avg_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(2.1666666666666665)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .avg() + .eq(Prop::F64(20.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_avg_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(45.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .avg() + .eq(Prop::F64(40.0)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal any: MIN ------ + #[test] + fn test_node_property_temporal_any_min_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .min() + .eq(Prop::U8(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_min_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .min() + .eq(Prop::U16(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_min_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .min() + .eq(Prop::U32(1)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_min_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .min() + .eq(Prop::U64(1)); + let expected = vec!["n1", "n10", "n2", "n3", "n4", "n5"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_min_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .min() + .eq(Prop::I32(-2)); + let expected = vec!["n6"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .min() + .eq(Prop::I32(10)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_min_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .min() + .eq(Prop::I64(-3)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .min() + .eq(Prop::I64(1)); + let expected = vec!["n1", "n5"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_min_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .min() + .eq(Prop::F32(1.0)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .min() + .eq(Prop::F32(10.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_min_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .min() + .eq(Prop::F64(30.0)); + let expected = vec!["n1", "n2", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .min() + .eq(Prop::F64(40.0)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal any: MAX ------ + #[test] + fn test_node_property_temporal_any_max_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .max() + .eq(Prop::U8(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .max() + .eq(Prop::U8(4)); + let expected = vec!["n1", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_max_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .max() + .eq(Prop::U16(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .max() + .eq(Prop::U16(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_max_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .max() + .eq(Prop::U32(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .max() + .eq(Prop::U32(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_max_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .max() + .eq(Prop::U64(4)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .max() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_max_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .max() + .eq(Prop::I32(3)); + let expected = vec!["n1", "n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .max() + .eq(Prop::I32(30)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_max_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .max() + .eq(Prop::I64(2)); + let expected = vec!["n10", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_max_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .max() + .eq(Prop::F32(3.5)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .max() + .eq(Prop::F32(30.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_max_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .max() + .eq(Prop::F64(50.0)); + let expected = vec!["n1", "n10", "n2", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal any: LEN ------ + #[test] + fn test_node_property_temporal_any_len_u8s() { + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .len() + .is_in(vec![Prop::U64(3)]); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u8s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_len_u16s() { + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_len_u32s() { + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_len_u64s() { + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_len_i32s() { + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4", "n6"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i32s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_len_i64s() { + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_i64s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_len_f32s() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .len() + .eq(Prop::U64(4)); + let expected = vec!["n1"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_property_temporal_any_len_f64s() { + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .len() + .eq(Prop::U64(2)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f64s") + .temporal() + .any() + .len() + .eq(Prop::U64(3)); + let expected = vec!["n1", "n2"]; + apply_assertion(filter, &expected); + } + + // ------ EMPTY LISTS ------ + #[test] + fn test_empty_list_agg() { + let filter = NodeFilter.property("p_u64s").sum().eq(Prop::U64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_u64s").avg().eq(Prop::F64(0.0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .last() + .min() + .eq(Prop::U64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s") + .temporal() + .first() + .max() + .eq(Prop::U64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_u64s").len().eq(Prop::U64(0)); + let expected: Vec<&str> = vec!["n7"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.metadata("p_u64s").len().eq(Prop::U64(0)); + let expected: Vec<&str> = vec!["n6"]; + apply_assertion(filter, &expected); + } + + // ------ Unsupported filter operations ------ + #[test] + fn test_unsupported_filter_ops_agg() { + let filter = NodeFilter.property("p_u64s").sum().starts_with("abc"); + let expected: &str = "Operator STARTS_WITH is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").avg().ends_with("abc"); + let expected: &str = "Operator ENDS_WITH is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").min().is_none(); + let expected: &str = "Operator IS_NONE is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").max().is_some(); + let expected: &str = "Operator IS_SOME is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").len().contains("abc"); + let expected: &str = "Operator CONTAINS is not supported with list aggregation"; + apply_assertion_err(filter, expected); + + let filter = NodeFilter.property("p_u64s").sum().not_contains("abc"); + let expected: &str = "Operator NOT_CONTAINS is not supported with list aggregation"; + apply_assertion_err(filter, expected); + } + + // --------------- OVERFLOW --------------- + #[test] + fn test_max_value_agg() { + let filter = NodeFilter + .property("p_u64s_max") + .max() + .eq(Prop::U64(u64::MAX)); + let expected: Vec<&str> = vec!["n5", "n1"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u64s_min") + .min() + .eq(Prop::U64(u64::MIN)); + let expected: Vec<&str> = vec!["n5"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_u8s_max").sum().eq(Prop::U64(510)); + let expected: Vec<&str> = vec!["n1"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u16s_max") + .sum() + .eq(Prop::U64(131070)); + let expected: Vec<&str> = vec!["n1"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_u32s_max") + .sum() + .eq(Prop::U64(8589934590)); + let expected: Vec<&str> = vec!["n1"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_u64s_max").sum().gt(Prop::U64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + // AVG is computed in f64 even if SUM overflowed. + let avg = (u64::MAX as f64 + 1.0) / 2.0; + let filter = NodeFilter.property("p_u64s_max").avg().eq(avg); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter.property("p_i64s_max").sum().gt(Prop::I64(0)); + let expected: Vec<&str> = vec![]; + apply_assertion(filter, &expected); + + // AVG is computed in f64 even if SUM overflowed. + let avg = (i64::MAX as f64 + 1.0) / 2.0; + let filter = NodeFilter.property("p_i64s_max").avg().eq(avg); + let expected = vec!["n5"]; + apply_assertion(filter, &expected); + } + + // ------ Property: any ------ + #[test] + fn test_node_property_any() { + let filter = NodeFilter.property("p_u8s").any().eq(Prop::U8(3)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Property: all ------ + #[test] + fn test_node_property_all() { + let filter = NodeFilter + .property("p_bools_all") + .all() + .eq(Prop::Bool(true)); + let expected = vec!["n10", "n4"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: any ------ + #[test] + fn test_node_metadata_any() { + let filter = NodeFilter.metadata("p_u64s").any().eq(Prop::U64(1)); + let expected = vec!["n10", "n7"]; + apply_assertion(filter, &expected); + } + + // ------ Metadata: all ------ + #[test] + fn test_node_metadata_all() { + let filter = NodeFilter.metadata("p_strs").all().eq("a"); + let expected = vec!["n6", "n7"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal First: any ------ + #[test] + fn test_node_temporal_property_first_any() { + let filter = NodeFilter + .property("p_bools") + .temporal() + .first() + .any() + .eq(false); + let expected = vec!["n1", "n10", "n2", "n3", "n4"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal First: all ------ + #[test] + fn test_node_temporal_property_first_all() { + let filter = NodeFilter + .property("p_bools_all") + .temporal() + .first() + .all() + .eq(true); + let expected = vec!["n10", "n4"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal last: any ------ + #[test] + fn test_node_temporal_property_last_any() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .last() + .any() + .eq(Prop::F32(3.5)); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal last: all ------ + #[test] + fn test_node_temporal_property_last_all() { + let filter = NodeFilter + .property("p_bools_all") + .temporal() + .last() + .all() + .eq(true); + let expected = vec!["n10", "n4"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal Any: any ------ + #[test] + fn test_node_temporal_property_any_any() { + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .any() + .eq(Prop::F32(3.5)); + let expected = vec!["n1", "n10", "n3", "n4"]; + apply_assertion(filter, &expected); + + let filter = NodeFilter + .property("p_f32s") + .temporal() + .any() + .any() + .eq(Prop::F32(30.0)); + let expected = vec!["n4"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal Any: all ------ + #[test] + fn test_node_temporal_property_any_all() { + let filter = NodeFilter + .property("p_bools") + .temporal() + .any() + .all() + .eq(false); + let expected = vec!["n2", "n4"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_nested_list_property_all_all_all_any() { + let filter = NodeFilter + .property("nested_list") + .all() + .all() + .all() + .any() + .gt(45.0); + + let expected = vec!["n1", "n3"]; + apply_assertion(filter, &expected); + } + + #[test] + fn test_node_nested_list_temporal_property_all_all_all_all_any() { + let filter = NodeFilter + .property("nested_list") + .temporal() + .all() + .all() + .all() + .all() + .any() + .gt(45.0); + + let expected = vec!["n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal All: any ------ + #[test] + fn test_node_temporal_property_all_any() { + let filter = NodeFilter + .property("p_bools") + .temporal() + .all() + .any() + .eq(true); + let expected = vec!["n1", "n10", "n3"]; + apply_assertion(filter, &expected); + } + + // ------ Temporal All: all ------ + #[test] + fn test_node_temporal_property_all_all() { + let filter = NodeFilter + .property("p_bools_all") + .temporal() + .all() + .all() + .eq(true); + let expected = vec!["n4", "n10"]; + apply_assertion(filter, &expected); + } +} + +#[cfg(test)] +mod test_edge_filter { + use crate::{ + init_edges_graph, init_edges_graph_with_num_ids, init_edges_graph_with_str_ids, + init_edges_graph_with_str_ids_del, init_nodes_graph, IdentityGraphTransformer, + }; + use raphtory::db::graph::views::filter::model::{ + edge_filter::EdgeFilter, + node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, + property_filter::ops::{ListAggOps, PropertyFilterOps}, + ComposableFilter, EdgeViewFilterOps, PropertyFilterFactory, TemporalPropertyFilterFactory, + ViewWrapOps, + }; + use raphtory_test_utils::assertions::{ + assert_filter_edges_results, assert_search_edges_results, assert_select_edges_results, + TestGraphVariants, TestVariants, + }; + + #[test] + fn test_filter_edges_src_property_eq() { + let filter = EdgeFilter::src().property("p10").eq("Paper_airplane"); + let expected_results = vec!["1->2", "3->1"]; + let g = |g| init_edges_graph(init_nodes_graph(g)); + assert_filter_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_src_property_temporal_eq() { + let filter = EdgeFilter::src() + .property("p30") + .temporal() + .first() + .eq("Old_boat"); + let expected_results = vec!["2->1", "2->3"]; + let g = |g| init_edges_graph(init_nodes_graph(g)); + assert_filter_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_src_metadata_eq() { + let filter = EdgeFilter::src().metadata("m1").eq("pometry"); + let expected_results = vec!["1->2"]; + let g = |g| init_edges_graph(init_nodes_graph(g)); + assert_filter_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + g, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_eq() { + let filter = EdgeFilter::src().name().eq("3"); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_ne() { + let filter = EdgeFilter::src().name().ne("1"); + let expected_results = vec![ + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_in() { + let filter = EdgeFilter::src().name().is_in(vec!["1"]); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src().name().is_in(vec!["1", "2"]); + let expected_results = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_not_in() { + let filter = EdgeFilter::src().name().is_not_in(vec!["1"]); + let expected_results = vec![ + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_eq() { + let filter = EdgeFilter::dst().name().eq("2"); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_ne() { + let filter = EdgeFilter::dst().name().ne("2"); + let expected_results = vec![ + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_in() { + let filter = EdgeFilter::dst().name().is_in(vec!["2"]); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst().name().is_in(vec!["2", "3"]); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_not_in() { + let filter = EdgeFilter::dst().name().is_not_in(vec!["1"]); + let expected_results = vec![ + "1->2", + "2->3", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_dst_starts_with() { + let filter = EdgeFilter::src().name().starts_with("Joh"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src().name().starts_with("Joker"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst().name().starts_with("Jimmy"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst().name().starts_with("Tango"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_dst_ends_with() { + let filter = EdgeFilter::src().name().ends_with("Mayer"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src().name().ends_with("Cruise"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst().name().ends_with("Page"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst().name().ends_with("Cruise"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_contains() { + let filter = EdgeFilter::src().name().contains("Mayer"); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_contains_not() { + let filter = EdgeFilter::src().name().not_contains("Mayer"); + let expected_results: Vec<&str> = + vec!["1->2", "2->1", "2->3", "3->1", "David Gilmour->John Mayer"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_fuzzy_search() { + let filter = EdgeFilter::src().name().fuzzy_search("John", 2, true); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src().name().fuzzy_search("John", 2, false); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src().name().fuzzy_search("John May", 2, false); + let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_not_src() { + let filter = EdgeFilter::src().name().is_not_in(vec!["1"]).not(); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_id_eq() { + let filter = EdgeFilter::src().id().eq("3"); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src().id().eq(3); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_id_eq() { + let filter = EdgeFilter::dst().id().eq("3"); + let expected_results = vec!["2->3"]; + // assert_filter_edges_results( + // init_edges_graph, + // IdentityGraphTransformer, + // filter.clone(), + // &expected_results, + // TestVariants::All, + // ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst().id().eq(3); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_id_ne() { + let filter = EdgeFilter::src().id().ne("3"); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src().id().ne(3); + let expected_results = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_id_ne() { + let filter = EdgeFilter::dst().id().ne("3"); + let expected_results = vec![ + "1->2", + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst().id().ne(3); + let expected_results = vec!["1->2", "2->1", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_id_src_is_in() { + let filter = EdgeFilter::src().id().is_in(vec!["3"]); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src().id().is_in(vec![3]); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_id_dst_is_in() { + let filter = EdgeFilter::dst().id().is_in(vec!["3"]); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst().id().is_in(vec![3]); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_id_src_is_not_in() { + let filter = EdgeFilter::src().id().is_not_in(vec!["3"]); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src().id().is_not_in(vec![3]); + let expected_results = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_id_dst_is_not_in() { + let filter = EdgeFilter::dst().id().is_not_in(vec!["3"]); + let expected_results = vec![ + "1->2", + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst().id().is_not_in(vec![3]); + let expected_results = vec!["1->2", "2->1", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_id_lt() { + let filter = EdgeFilter::src().id().lt(3); + let expected_results = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_id_lt() { + let filter = EdgeFilter::dst().id().lt(3); + let expected_results = vec!["1->2", "2->1", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_id_le() { + let filter = EdgeFilter::src().id().le(3); + let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_id_le() { + let filter = EdgeFilter::dst().id().le(3); + let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_id_gt() { + let filter = EdgeFilter::src().id().gt(1); + let expected_results = vec!["2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_id_gt() { + let filter = EdgeFilter::dst().id().gt(1); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_id_ge() { + let filter = EdgeFilter::src().id().ge(1); + let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_id_ge() { + let filter = EdgeFilter::dst().id().ge(1); + let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; + assert_filter_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_num_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_name_starts_with() { + let filter = EdgeFilter::src().name().starts_with("Tw"); + let expected_results = vec!["Two->One", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_id_ends_with() { + let filter = EdgeFilter::src().id().ends_with("don"); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_id_contains() { + let filter = EdgeFilter::src().id().contains("don"); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_id_contains() { + let filter = EdgeFilter::dst().id().contains("Par"); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_name_not_contains() { + let filter = EdgeFilter::dst().name().not_contains("Par"); + let expected_results = vec![ + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + "Three->One", + "Two->One", + "Two->Three", + ]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_src_id_is_in() { + let filter = EdgeFilter::src().id().is_in(["Two"]); + let expected_results = vec!["Two->One", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_dst_id_is_not_in() { + let filter = EdgeFilter::dst().id().is_not_in(["One"]); + let expected_results = vec![ + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + "London->Paris", + "Two->Three", + ]; + assert_filter_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_is_active_edge_window() { + let filter = EdgeFilter.window(1, 3).is_active(); + let expected_results = vec!["London->Paris", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_is_active_edge_after() { + let filter = EdgeFilter.after(3).is_active(); + let expected_results = vec![ + "Bangalore->Bangalore", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_is_active_edge_before() { + let filter = EdgeFilter.before(3).is_active(); + let expected_results = vec!["London->Paris", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_is_active_edge_snapshot_latest() { + let filter = EdgeFilter.snapshot_latest().is_active(); + let expected_results = vec!["Bangalore->Bangalore"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_is_valid_edge_window() { + let filter = EdgeFilter.window(1, 3).is_valid(); + let expected_results = vec!["London->Paris", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + + let filter = EdgeFilter.window(1, 4).is_valid(); + let expected_results = vec!["Three->One", "Two->One", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + } + + #[test] + fn test_is_valid_edge_snapshot_at() { + let filter = EdgeFilter.snapshot_at(2).is_valid(); + let expected_results = vec!["London->Paris", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + + let filter = EdgeFilter.snapshot_at(3).is_valid(); + let expected_results = vec!["Three->One", "Two->One", "Two->Three"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + } + + #[test] + fn test_is_valid_edge_snapshot_latest() { + let filter = EdgeFilter.snapshot_latest().is_valid(); + let expected_results = vec![ + "Bangalore->Bangalore", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + "Three->One", + "Two->One", + "Two->Three", + ]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestGraphVariants::PersistentGraph, + ); + } + + // Disk graph doesn't support deletions + #[test] + fn test_is_deleted_edge_after() { + let filter = EdgeFilter.after(1).is_deleted(); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + // Disk graph doesn't support deletions + #[test] + fn test_is_deleted_edge_before() { + let filter = EdgeFilter.before(4).is_deleted(); + let expected_results = vec!["London->Paris"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_is_self_loop_edge_window() { + let filter = EdgeFilter.window(1, 3).is_self_loop(); + let expected_results = vec![]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.window(1, 6).is_self_loop(); + let expected_results = vec!["Bangalore->Bangalore"]; + assert_filter_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_select_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph_with_str_ids_del, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } +} + +#[cfg(test)] +mod test_edge_property_filter { + use crate::{init_edges_graph, init_edges_graph2, IdentityGraphTransformer}; + use raphtory::db::graph::views::filter::model::{ + edge_filter::EdgeFilter, + property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, + ComposableFilter, PropertyFilterFactory, TemporalPropertyFilterFactory, ViewWrapOps, + }; + + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_test_utils::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, TestVariants, + }; + + #[test] + fn test_filter_edges_for_property_eq() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").eq(2u64); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p30").temporal().first().eq("Old_boat"); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p20").temporal().all().eq("Gold_ship"); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_ne() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").ne(2u64); + let expected_results = vec![ + "1->2", + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p30").temporal().first().ne("Old_boat"); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p30").temporal().all().ne("Classic"); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_lt() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").lt(10u64); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p2").temporal().first().lt(5u64); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p2").temporal().all().lt(10u64); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_le() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").le(6u64); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p2").temporal().first().le(3u64); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p2").temporal().all().le(5u64); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_gt() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").gt(2u64); + let expected_results = vec![ + "1->2", + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p2").temporal().first().gt(5u64); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p2").temporal().all().gt(5u64); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_ge() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").ge(2u64); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p2").temporal().first().ge(6u64); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p2").temporal().all().ge(6u64); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_in() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").is_in(vec![Prop::U64(6)]); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p2") + .is_in(vec![Prop::U64(2), Prop::U64(6)]); + let expected_results = vec![ + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p2") + .temporal() + .first() + .is_in(vec![Prop::U64(6)]); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p2") + .temporal() + .all() + .is_in(vec![Prop::U64(6)]); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_not_in() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p2") + .temporal() + .first() + .is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p2") + .temporal() + .all() + .is_not_in(vec![Prop::U64(6)]); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_is_some() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p2").is_some(); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p2").temporal().first().is_some(); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_is_none() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. + let filter = EdgeFilter.property("p2").is_none(); + let expected_results = Vec::<&str>::new(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("p2").temporal().first().is_none(); + let expected_results = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_starts_with() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p10").starts_with("Pa"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .any() + .starts_with("Pape"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .starts_with("Paper"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .starts_with("Traffic"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p30") + .temporal() + .first() + .starts_with("Old"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p20") + .temporal() + .all() + .starts_with("Gold"); + let expected_results: Vec<&str> = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_ends_with() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p10").ends_with("lane"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .any() + .ends_with("ship"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .ends_with("ane"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .ends_with("marcus"); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p20") + .temporal() + .first() + .ends_with("boat"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p20") + .temporal() + .all() + .ends_with("ship"); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_contains() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p10").contains("Paper"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .any() + .contains("Paper"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .contains("Paper"); + let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p20") + .temporal() + .first() + .contains("boat"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p20").temporal().all().contains("ship"); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_for_property_contains_not() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("p10").not_contains("ship"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .any() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p10") + .temporal() + .last() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["1->2", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p20") + .temporal() + .first() + .not_contains("boat"); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p30") + .temporal() + .all() + .not_contains("ship"); + let expected_results: Vec<&str> = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_filter_edges_by_fuzzy_search() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. + // TODO: Enable these test for event_disk_graph, persistent_disk_graph once string property is fixed. + let filter = EdgeFilter.property("p1").fuzzy_search("shiv", 2, true); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = EdgeFilter.property("p1").fuzzy_search("ShiV", 2, true); + let expected_results: Vec<&str> = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = EdgeFilter.property("p1").fuzzy_search("shiv", 2, false); + let expected_results: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_filter_edges_for_not_property() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. + let filter = EdgeFilter.property("p2").ne(2u64).not(); + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_window_filter() { + let filter = EdgeFilter + .window(1, 3) + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .window(1, 5) + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let expected_results = vec![ + "1->2", + "2->3", + "3->1", + "2->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_window_filter_on_non_temporal_property() { + let filter1 = EdgeFilter.window(1, 2).property("p1").eq("shivam_kapoor"); + let filter2 = EdgeFilter + .window(100, 200) + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter1.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter1.clone(), + &expected_results, + TestVariants::All, + ); + + let expected_results = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter2 = EdgeFilter + .window(100, 200) + .property("p1") + .eq("shivam_kapoor"); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter2.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_window_filter_any_all_over_window() { + let filter_any = EdgeFilter + .window(2, 4) + .property("p20") + .temporal() + .any() + .eq("Gold_boat"); + + let expected_any = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_any.clone(), + &expected_any, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_any.clone(), + &expected_any, + TestVariants::All, + ); + + let filter_all = EdgeFilter + .window(2, 4) + .property("p20") + .temporal() + .all() + .eq("Gold_boat"); + + let expected_all: Vec<&str> = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_all.clone(), + &expected_all, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_all.clone(), + &expected_all, + TestVariants::All, + ); + } + + #[test] + fn test_edges_window_filter_and() { + let filter1 = EdgeFilter + .window(3, 6) + .property("p10") + .temporal() + .any() + .eq("Paper_airplane"); + + let filter2 = EdgeFilter + .window(3, 6) + .property("p2") + .temporal() + .sum() + .eq(6u64); + + let filter = filter1.and(filter2); + + let expected_results = vec!["2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_layer_filter() { + let filter = EdgeFilter + .layer("fire_nation") + .property("p2") + .temporal() + .sum() + .ge(2u64); + + let expected_results = vec!["1->2", "3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_at_filter() { + // Only time=2 contributes; edge 2->3 has p2=2 at t=2 + let filter = EdgeFilter.at(2).property("p2").temporal().sum().eq(2u64); + + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + // Only time=3 contributes; edge 3->1 has p2=6 at t=3 + let filter = EdgeFilter.at(3).property("p2").temporal().sum().eq(6u64); + + let expected_results = vec!["3->1", "2->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_after_filter() { + // after(2) means t >= 3 + let filter = EdgeFilter.after(2).property("p2").temporal().sum().ge(6u64); + + // At t=3: 3->1 and 2->1 have p2=6 + // At t=4: David->John and John->Jimmy have p2=6 + let expected_results = vec![ + "3->1", + "2->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_before_filter() { + // before(3) means t <= 2 + let filter = EdgeFilter + .before(3) + .property("p2") + .temporal() + .sum() + .eq(2u64); + + // Only t=2 contributes for p2=2 -> 2->3 + let expected_results = vec!["2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + + // And p2=6 edges shouldn't match, because their p2=6 lives at t=3+. + let filter = EdgeFilter + .before(3) + .property("p2") + .temporal() + .sum() + .eq(6u64); + + let expected_results = vec![]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_latest_filter() { + // At latest time (currently t=4), only the t=4 edges exist in the Event graph. + // Use EventOnly so the expectation is stable and matches node-style. + let filter = EdgeFilter.latest().property("p2").eq(6u64); + + let expected_results = vec!["David Gilmour->John Mayer", "John Mayer->Jimmy Page"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_snapshot_at_semantics_event_graph() { + let t = 2; + + let filter_snapshot = EdgeFilter + .snapshot_at(t) + .property("p2") + .temporal() + .sum() + .eq(2u64); + + let filter_before = EdgeFilter + .before(t + 1) + .property("p2") + .temporal() + .sum() + .eq(2u64); + + let expected_results = vec!["2->3"]; + + // snapshot_at + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot, + &expected_results, + TestVariants::EventOnly, + ); + + // before(t+1) + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_before.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_before, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_snapshot_at_semantics_persistent_graph() { + let t = 2; + + let filter_snapshot = EdgeFilter + .snapshot_at(t) + .property("p2") + .temporal() + .sum() + .eq(2u64); + + let filter_at = EdgeFilter.at(t).property("p2").temporal().sum().eq(2u64); + + let expected_results = vec!["2->3"]; + + // snapshot_at + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot, + &expected_results, + TestVariants::PersistentOnly, + ); + + // at(t) + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_at.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_at, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_snapshot_latest_semantics_event_graph() { + let filter_snapshot_latest = EdgeFilter + .snapshot_latest() + .property("p2") + .temporal() + .sum() + .ge(6u64); + + let filter_noop = EdgeFilter.property("p2").temporal().sum().ge(6u64); + + // Across the whole event history, p2=6 appears at t=3 and t=4. + let expected_results = vec![ + "3->1", + "2->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + + // snapshot_latest + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot_latest.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot_latest, + &expected_results, + TestVariants::EventOnly, + ); + + // no-op baseline + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_noop.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_noop, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_snapshot_latest_semantics_persistent_graph() { + let filter_snapshot_latest = EdgeFilter.snapshot_latest().property("p2").eq(6u64); + + let filter_latest = EdgeFilter.latest().property("p2").eq(6u64); + + // In persistent latest state at t=4, these edges have p2=6: + // - t=3 edges: 3->1, 2->1 + // - t=4 edges: David->John, John->Jimmy + let expected_results = vec![ + "3->1", + "2->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + + // snapshot_latest + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot_latest.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_snapshot_latest, + &expected_results, + TestVariants::PersistentOnly, + ); + + // latest + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_latest.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter_latest, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_layer_then_window_ordering() { + // In layer "fire_nation" within window [1,3), edge 1->2 matches p1 == "shivam_kapoor". + let filter = EdgeFilter + .layer("fire_nation") + .window(1, 3) + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1->2"]; + + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_window_then_layer_ordering() { + // Same semantics, reversed chaining order. + let filter = EdgeFilter + .window(1, 3) + .layer("fire_nation") + .property("p1") + .eq("shivam_kapoor"); + + let expected_results = vec!["1->2"]; + + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_latest_layer() { + let filter = EdgeFilter + .latest() + .layer("fire_nation") + .property("p2") + .temporal() + .last() + .eq(7u64); + + let expected_results = vec![]; + + assert_filter_edges_results( + init_edges_graph2, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_layer_latest() { + let filter = EdgeFilter + .layer("fire_nation") + .latest() + .property("p2") + .temporal() + .last() + .eq(7u64); + + let expected_results = vec!["1->2"]; + + assert_filter_edges_results( + init_edges_graph2, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } +} + +#[cfg(test)] +mod test_edge_composite_filter { + use raphtory::db::graph::views::filter::model::{ + edge_filter::EdgeFilter, node_filter::ops::NodeFilterOps, + property_filter::ops::PropertyFilterOps, ComposableFilter, PropertyFilterFactory, + TryAsCompositeFilter, + }; + use raphtory_test_utils::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, TestVariants, + }; + + use crate::{init_edges_graph, IdentityGraphTransformer}; + + #[test] + fn test_filter_edge_for_src_dst() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter::src() + .name() + .eq("3") + .and(EdgeFilter::dst().name().eq("1")); + let expected_results = vec!["3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_unique_results_from_composite_filters() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter + .property("p2") + .ge(2u64) + .and(EdgeFilter.property("p2").ge(1u64)); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter + .property("p2") + .ge(2u64) + .or(EdgeFilter.property("p2").ge(5u64)); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_composite_filter_edges() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. + // TODO: Enable these test for event_disk_graph, persistent_disk_graph once string property is fixed. + let filter = EdgeFilter + .property("p2") + .eq(2u64) + .and(EdgeFilter.property("p1").eq("kapoor")); + let expected_results = Vec::<&str>::new(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p2") + .eq(2u64) + .or(EdgeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1->2", "2->3"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter.property("p1").eq("pometry").or(EdgeFilter + .property("p2") + .eq(6u64) + .and(EdgeFilter.property("p3").eq(1u64))); + let expected_results = vec![ + "2->1", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src() + .name() + .eq("13") + .and(EdgeFilter.property("p1").eq("prop1")); + let expected_results = Vec::<&str>::new(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter + .property("p2") + .eq(4u64) + .and(EdgeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src() + .name() + .eq("1") + .and(EdgeFilter.property("p1").eq("shivam_kapoor")); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::dst() + .name() + .eq("1") + .and(EdgeFilter.property("p2").eq(6u64)); + let expected_results = vec!["2->1", "3->1"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = EdgeFilter::src() + .name() + .eq("1") + .and(EdgeFilter.property("p1").eq("shivam_kapoor")) + .or(EdgeFilter.property("p3").eq(5u64)); + let expected_results = vec!["1->2"]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let filter = filter.try_as_composite_edge_filter().unwrap(); + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_not_composite_filter_edges() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. + let filter = EdgeFilter::src() + .name() + .eq("13") + .and(EdgeFilter.property("p1").eq("prop1")) + .not(); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter::src() + .name() + .eq("13") + .and(EdgeFilter.property("p1").eq("prop1").not()) + .not(); + let expected_results = vec![ + "1->2", + "2->1", + "2->3", + "3->1", + "David Gilmour->John Mayer", + "John Mayer->Jimmy Page", + ]; + assert_filter_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + assert_search_edges_results( + init_edges_graph, + IdentityGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } +} diff --git a/raphtory/tests/test_history.rs b/raphtory-test-utils/tests/test_history.rs similarity index 100% rename from raphtory/tests/test_history.rs rename to raphtory-test-utils/tests/test_history.rs diff --git a/raphtory-test-utils/tests/test_layers.rs b/raphtory-test-utils/tests/test_layers.rs new file mode 100644 index 0000000000..25709b56f8 --- /dev/null +++ b/raphtory-test-utils/tests/test_layers.rs @@ -0,0 +1,778 @@ +use itertools::Itertools; +use proptest::proptest; +use raphtory::{ + db::graph::{graph::assert_graph_equal, views::deletion_graph::PersistentGraph}, + prelude::*, +}; +use raphtory_api::core::entities::GID; +use raphtory_test_utils::{ + test_storage, + test_utils::{build_graph, build_graph_layer, build_graph_strat, GraphFixture}, +}; +use serde_json::json; + +#[test] +fn prop_test_layering() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, false), layer in proptest::sample::subsequence(&["_default", "a", "b"], 0..3))| { + let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); + let g = Graph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer.clone()); + assert_graph_equal(&g_layer, &g_layer_expected); + }) +} + +#[test] +fn test_node_explicit_node_additions() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{"10":{"props":{"t_props":[[0,[]]],"c_props":[]},"node_type":null}},"edges":[]})).unwrap(); + let layer = []; + let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); + let g = Graph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer.clone()); + + assert_graph_equal(&g_layer, &g_layer_expected); +} + +#[test] +fn test_failure() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[3,9,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[9,3,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let layer = ["_default", "b"]; + let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); + let g = Graph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer.clone()); + + assert_graph_equal(&g_layer, &g_layer_expected); +} + +#[test] +fn test_failure2() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let layer = ["_default", "b"]; + let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); + let g = Graph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer.clone()); + + assert_graph_equal(&g_layer, &g_layer_expected); +} + +#[test] +fn test_failure3() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,1,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let layer = ["_default", "b"]; + let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); + let g = Graph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer.clone()); + + assert_graph_equal(&g_layer, &g_layer_expected); +} + +#[test] +fn prop_test_layering_persistent_graph() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), layer in proptest::sample::subsequence(&["_default", "a", "b"], 0..3))| { + let g_layer_expected = PersistentGraph::from(build_graph_layer(&graph_f, &layer)); + let g = PersistentGraph::from(build_graph(&graph_f)); + let g_layer = g.valid_layers(layer); + assert_graph_equal(&g_layer, &g_layer_expected); + }) +} + +#[test] +fn test_layer_node() { + let graph = Graph::new(); + + graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(0, 2, 3, NO_PROPS, Some("layer2")).unwrap(); + graph.add_edge(3, 2, 4, NO_PROPS, Some("layer1")).unwrap(); + graph.add_edge(1, 4, 1, NO_PROPS, Some("layer3")).unwrap(); + + test_storage!(&graph, |graph| { + let neighbours = graph + .layers(vec!["layer1", "layer2"]) + .unwrap() + .node(1) + .unwrap() + .neighbours() + .into_iter() + .collect_vec(); + assert_eq!( + neighbours[0] + .layers("layer2") + .unwrap() + .edges() + .id() + .collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + assert_eq!( + graph + .layers("layer2") + .unwrap() + .node(neighbours[0].name()) + .unwrap() + .edges() + .id() + .collect_vec(), + vec![(GID::U64(2), GID::U64(3))] + ); + let mut edges = graph + .layers("layer1") + .unwrap() + .node(neighbours[0].name()) + .unwrap() + .edges() + .id() + .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) + .collect_vec(); + edges.sort(); + assert_eq!(edges, vec![(1, 2), (2, 4)]); + let mut edges = graph + .layers("layer1") + .unwrap() + .edges() + .id() + .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) + .collect_vec(); + edges.sort(); + assert_eq!(edges, vec![(1, 2), (2, 4)]); + let mut edges = graph + .layers(vec!["layer1", "layer2"]) + .unwrap() + .edges() + .id() + .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) + .collect_vec(); + edges.sort(); + assert_eq!(edges, vec![(1, 2), (2, 3), (2, 4)]); + + let mut edges = graph + .layers(["layer1", "layer3"]) + .unwrap() + .window(0, 2) + .edges() + .id() + .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) + .collect_vec(); + edges.sort(); + assert_eq!(edges, vec![(1, 2), (4, 1)]); + }); +} + +#[test] +fn layering_tests() { + let graph = Graph::new(); + let e1 = graph.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, Some("2")).unwrap(); + + println!("edge: {e1:?}"); + // FIXME: this is weird, see issue #1458 + assert!(e1.has_layer("2")); + let history = e1.layers("2").unwrap().history(); + println!("history: {:?}", history); + assert!(e1.layers("2").unwrap().history().is_empty()); + + test_storage!(&graph, |graph| { + let e = graph.edge(1, 2).unwrap(); + // layers with non-existing layers errors + assert!(e.layers(["1", "3"]).is_err()); + // valid_layers ignores non-existing layers + assert_eq!(e.valid_layers(["1", "3"]).layer_names(), ["1"]); + assert!(e.has_layer("1")); + assert!(e.has_layer("2")); + assert!(!e.has_layer("3")); + assert!(e.valid_layers("1").has_layer("1")); + assert!(!e.valid_layers("1").has_layer("2")); + }); +} + +pub mod test_filters_layer_graph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::{layer_graph::LayeredGraph, window_graph::WindowedGraph}, + }, + prelude::{LayerOps, TimeOps}, + }; + use raphtory_test_utils::assertions::GraphTransformer; + use std::ops::Range; + + struct LayeredGraphTransformer(Vec); + + impl GraphTransformer for LayeredGraphTransformer { + type Return = LayeredGraph; + fn apply(&self, graph: G) -> Self::Return { + graph.layers(self.0.clone()).unwrap() + } + } + + struct LayeredGraphWindowTransformer(Vec, Range); + + impl GraphTransformer for LayeredGraphWindowTransformer { + type Return = WindowedGraph>; + fn apply(&self, graph: G) -> Self::Return { + graph + .layers(self.0.clone()) + .unwrap() + .window(self.1.start, self.1.end) + } + } + + pub mod test_nodes_filters_layer_graph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::property_filter::ops::PropertyFilterOps, + }, + prelude::AdditionOps, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + use crate::test_filters_layer_graph::{ + LayeredGraphTransformer, LayeredGraphWindowTransformer, + }; + use raphtory::{ + db::graph::views::filter::model::PropertyFilterFactory, prelude::NodeFilter, + }; + + use raphtory_test_utils::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, + TestVariants, + }; + fn init_graph(graph: G) -> G { + let edges = vec![ + (6, "N1", "N2", vec![("p1", Prop::U64(2u64))], Some("layer1")), + (7, "N1", "N2", vec![("p1", Prop::U64(1u64))], Some("layer2")), + (6, "N2", "N3", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (7, "N2", "N3", vec![("p1", Prop::U64(2u64))], Some("layer2")), + (8, "N3", "N4", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (9, "N4", "N5", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (5, "N5", "N6", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (6, "N5", "N6", vec![("p1", Prop::U64(2u64))], Some("layer2")), + (5, "N6", "N7", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (6, "N6", "N7", vec![("p1", Prop::U64(1u64))], Some("layer2")), + (3, "N7", "N8", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (5, "N7", "N8", vec![("p1", Prop::U64(1u64))], Some("layer2")), + (3, "N8", "N1", vec![("p1", Prop::U64(1u64))], Some("layer1")), + (4, "N8", "N1", vec![("p1", Prop::U64(2u64))], Some("layer2")), + ]; + + for (id, src, tgt, props, layer) in &edges { + graph + .add_edge(*id, src, tgt, props.clone(), *layer) + .unwrap(); + } + + let nodes = vec![ + (6, "N1", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + (7, "N1", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (6, "N2", vec![("p1", Prop::U64(1u64))], Some("water_tribe")), + (7, "N2", vec![("p1", Prop::U64(2u64))], Some("water_tribe")), + (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (5, "N5", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (6, "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (6, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (3, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (4, "N8", vec![("p1", Prop::U64(2u64))], Some("fire_nation")), + ]; + + for (id, name, props, label) in &nodes { + graph + .add_node(*id, name, props.clone(), *label, None) + .unwrap(); + } + + graph + } + + // Layers don't have any effect on the number of nodes in a graph. + // In other words, it is as good as applying no layer filters. + #[test] + fn test_nodes_filters() { + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = NodeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2", "N5", "N8"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = NodeFilter.property("p1").lt(2u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = NodeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2", "N5", "N8"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_filters_w() { + // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = NodeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = NodeFilter.property("p1").lt(2u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_filters_pg_w() { + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = NodeFilter.property("p1").lt(2u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = NodeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2", "N5", "N8"]; + assert_filter_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + } + + mod test_edges_filters_layer_graph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }, + }, + prelude::{AdditionOps, EdgeFilter}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + use raphtory_test_utils::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, + TestVariants, + }; + + use crate::test_filters_layer_graph::{ + LayeredGraphTransformer, LayeredGraphWindowTransformer, + }; + + fn init_graph(graph: G) -> G { + let edges = vec![ + (6, "N1", "N2", 2u64, "layer1"), + (7, "N1", "N2", 1u64, "layer2"), + (6, "N2", "N3", 1u64, "layer1"), + (7, "N2", "N3", 2u64, "layer2"), + (8, "N3", "N4", 1u64, "layer1"), + (9, "N4", "N5", 1u64, "layer1"), + (5, "N5", "N6", 1u64, "layer1"), + (6, "N5", "N6", 2u64, "layer2"), + (5, "N6", "N7", 1u64, "layer1"), + (6, "N6", "N7", 1u64, "layer2"), + (3, "N7", "N8", 1u64, "layer1"), + (5, "N7", "N8", 1u64, "layer2"), + (3, "N8", "N1", 1u64, "layer1"), + (4, "N8", "N1", 2u64, "layer2"), + ]; + + for (ts, src, dst, p1_val, layer) in edges { + graph + .add_edge(ts, src, dst, [("p1", Prop::U64(p1_val))], Some(layer)) + .unwrap(); + } + + graph + } + + #[test] + fn test_edges_filters() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = vec![ + "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N1", + ]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = EdgeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = EdgeFilter.property("p1").lt(2u64); + let expected_results = vec![ + "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N1", + ]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = EdgeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; + assert_filter_edges_results( + init_graph, + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphTransformer(layers), + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_filter_w() { + // Edge Property Semantics: + // 1. All property updates to an edge belong to a layer (or _default if no layer specified) + // 2. However, when asked for a value of a particular property for an edge, the latest update + // across all specified layers (or all layers if no layers specified) is returned! + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + // Edge Property Semantics: + // When filtering by specific layer, filter criteria (p1==1) and latest semantics is applicable + // only to that specific layer. + let layers: Vec = vec!["layer1".into()]; + let filter = EdgeFilter.property("p1").lt(2u64); + let expected_results = vec!["N2->N3", "N3->N4"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = EdgeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_w() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. + let layers: Vec = vec!["layer1".into(), "layer2".into()]; + let filter = EdgeFilter.property("p1").eq(1u64); + + // Why is the edge N8 -> N1 included in the results? + // The reason edge N8 -> N1 is included as part of the results because of following two semantic reasons: + // .add_edge(3, "N8", "N1", [("p1", Prop::U64(1u64))], Some("layer1")) + // .add_edge(4, "N8", "N1", [("p1", Prop::U64(2u64))], Some("layer2")) + // 1. As per layer graph semantics, every edge update belongs to a particular layer (or '_default' if no layer specified). + // This means the last_before is computed per layer and not across layers. In other words, when computing + // last_before for (N8->N1, layer1) and window(6, 9), t = 3 is the correct last before edge update timestamp and not t = 4 + // because t=4 edge update is in layer2. + // 2. Since the search is conducted across both the layers i.e., layer1 and layer2, the results are union of + // results from both layer1 and layer2. + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + + let layers: Vec = vec!["layer1".into()]; + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = vec!["N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N1"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + + let layers: Vec = vec!["layer2".into()]; + let filter = EdgeFilter.property("p1").ge(2u64); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; + assert_filter_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + init_graph, + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + } + } +} diff --git a/raphtory-test-utils/tests/test_materialize.rs b/raphtory-test-utils/tests/test_materialize.rs new file mode 100644 index 0000000000..d6ca5e4683 --- /dev/null +++ b/raphtory-test-utils/tests/test_materialize.rs @@ -0,0 +1,194 @@ +use itertools::Itertools; +use proptest::{arbitrary::any, proptest}; +use raphtory::{ + db::graph::graph::{assert_graph_equal, assert_graph_equal_timestamps}, + prelude::*, + // test_storage, + // test_utils::{build_edge_list, build_graph_from_edge_list}, +}; +use raphtory_api::core::storage::arc_str::OptionAsStr; +use raphtory_storage::core_ops::CoreGraphOps; +use raphtory_test_utils::{ + test_storage, + test_utils::{build_edge_list, build_graph_from_edge_list}, +}; +use std::ops::Range; + +#[test] +fn test_materialize() { + let g = Graph::new(); + g.add_edge(0, 1, 2, [("layer1", "1")], Some("1")).unwrap(); + g.add_edge(0, 1, 2, [("layer2", "2")], Some("2")).unwrap(); + + let gm = g.materialize().unwrap(); + + assert_graph_equal(&g, &gm); + assert_eq!( + gm.nodes().name().iter_values().collect::>(), + vec!["1", "2"] + ); + + assert!(g + .layers("2") + .unwrap() + .edge(1, 2) + .unwrap() + .properties() + .temporal() + .get("layer1") + .and_then(|prop| prop.latest()) + .is_none()); + + assert!(gm + .into_events() + .unwrap() + .layers("2") + .unwrap() + .edge(1, 2) + .unwrap() + .properties() + .temporal() + .get("layer1") + .and_then(|prop| prop.latest()) + .is_none()); +} + +#[test] +fn test_graph_properties() { + let g = Graph::new(); + g.add_properties(1, [("test", "test")]).unwrap(); + g.add_metadata([("test_metadata", "test2")]).unwrap(); + + test_storage!(&g, |g| { + let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); + }); +} + +#[test] +fn materialize_prop_test() { + proptest!(|(edges in build_edge_list(100, 100), w in any::>())| { + let g = build_graph_from_edge_list(&edges); + test_storage!(&g, |g| { + let gm = g.materialize().unwrap(); + assert_graph_equal_timestamps(&g, &gm); + let gw = g.window(w.start, w.end); + let gmw = gw.materialize().unwrap(); + assert_graph_equal_timestamps(&gw, &gmw); + }); + }) +} + +#[test] +fn test_subgraph() { + let g = Graph::new(); + g.add_node(0, 1, NO_PROPS, None, None).unwrap(); + g.add_node(0, 2, NO_PROPS, None, None).unwrap(); + g.add_node(0, 3, NO_PROPS, None, None).unwrap(); + g.add_node(0, 4, NO_PROPS, None, None).unwrap(); + g.add_node(0, 5, NO_PROPS, None, None).unwrap(); + + let nodes_subgraph = g.subgraph(vec![4, 5]); + assert_eq!( + nodes_subgraph + .nodes() + .name() + .iter_values() + .collect::>(), + vec!["4", "5"] + ); + let gm = nodes_subgraph.materialize().unwrap(); + assert_graph_equal(&nodes_subgraph, &gm); +} + +#[test] +fn test_exclude_nodes() { + let g = Graph::new(); + g.add_node(0, 1, NO_PROPS, None, None).unwrap(); + g.add_node(0, 2, NO_PROPS, None, None).unwrap(); + g.add_node(0, 3, NO_PROPS, None, None).unwrap(); + g.add_node(0, 4, NO_PROPS, None, None).unwrap(); + g.add_node(0, 5, NO_PROPS, None, None).unwrap(); + + let exclude_nodes_subgraph = g.exclude_nodes(vec![4, 5]); + assert_eq!( + exclude_nodes_subgraph + .nodes() + .name() + .iter_values() + .sorted() + .collect::>(), + vec!["1", "2", "3"] + ); + let gm = exclude_nodes_subgraph.materialize().unwrap(); + assert_graph_equal(&exclude_nodes_subgraph, &gm); +} + +#[test] +fn testing_node_types() { + let graph = Graph::new(); + graph.add_node(0, "A", NO_PROPS, None, None).unwrap(); + graph.add_node(1, "B", NO_PROPS, Some("H"), None).unwrap(); + + test_storage!(&graph, |graph| { + let node_a = graph.node("A").unwrap(); + let node_b = graph.node("B").unwrap(); + let node_a_type = node_a.node_type(); + let node_a_type_str = node_a_type.as_str(); + + assert_eq!(node_a_type_str, None); + assert_eq!(node_b.node_type().as_str(), Some("H")); + }); + + // Nodes with No type can be overwritten + let node_a = graph + .add_node(1, "A", NO_PROPS, Some("TYPEA"), None) + .unwrap(); + assert_eq!(node_a.node_type().as_str(), Some("TYPEA")); + + // Check that overwriting a node type returns an error + assert!(graph + .add_node(2, "A", NO_PROPS, Some("TYPEB"), None) + .is_err()); + // Double check that the type did not actually change + assert_eq!(graph.node("A").unwrap().node_type().as_str(), Some("TYPEA")); + // Check that the update is not added to the graph + let all_node_types = graph.get_all_node_types(); + assert_eq!(all_node_types.len(), 2); +} + +#[test] +fn changing_property_type_errors() { + let g = Graph::new(); + let props_0 = [("test", Prop::U64(1))]; + let props_1 = [("test", Prop::F64(0.1))]; + g.add_properties(0, props_0.clone()).unwrap(); + assert!(g.add_properties(1, props_1.clone()).is_err()); + + g.add_node(0, 1, props_0.clone(), None, None).unwrap(); + assert!(g.add_node(1, 1, props_1.clone(), None, None).is_err()); + + g.add_edge(0, 1, 2, props_0.clone(), None).unwrap(); + assert!(g.add_edge(1, 1, 2, props_1.clone(), None).is_err()); +} + +#[test] +fn test_edge_layer_properties() { + let g = Graph::new(); + g.add_edge(1, "A", "B", [("greeting", "howdy")], Some("layer 1")) + .unwrap(); + g.add_edge(2, "A", "B", [("greeting", "ola")], Some("layer 2")) + .unwrap(); + g.add_edge(2, "A", "B", [("greeting", "hello")], Some("layer 2")) + .unwrap(); + g.add_edge(3, "A", "B", [("greeting", "namaste")], Some("layer 3")) + .unwrap(); + + let edge_ab = g.edge("A", "B").unwrap(); + let props = edge_ab + .properties() + .iter() + .filter_map(|(k, v)| v.map(move |v| (k.to_string(), v.to_string()))) + .collect::>(); + assert_eq!(props, vec![("greeting".to_string(), "namaste".to_string())]); +} diff --git a/raphtory/tests/test_materialize_sf10.rs b/raphtory-test-utils/tests/test_materialize_sf10.rs similarity index 97% rename from raphtory/tests/test_materialize_sf10.rs rename to raphtory-test-utils/tests/test_materialize_sf10.rs index 89cc4874d4..c37e7c340a 100644 --- a/raphtory/tests/test_materialize_sf10.rs +++ b/raphtory-test-utils/tests/test_materialize_sf10.rs @@ -6,16 +6,15 @@ use raphtory::{ api::view::{materialize_impl, MaterializedGraph}, graph::graph::{assert_graph_equal_timestamps, graph_equal}, }, - prelude::{AdditionOps, DeletionOps, Graph, GraphViewOps, LayerOps, PropertyAdditionOps}, -}; -#[cfg(feature = "io")] -use raphtory::{ io::parquet_loaders::{ get_parquet_file_paths, load_edge_deletions_from_parquet, load_edge_metadata_from_parquet, load_edges_from_parquet, load_graph_props_from_parquet, load_node_metadata_from_parquet, load_nodes_from_parquet, }, - prelude::{ParquetDecoder, ParquetEncoder}, + prelude::{ + AdditionOps, DeletionOps, Graph, GraphViewOps, LayerOps, ParquetDecoder, ParquetEncoder, + PropertyAdditionOps, + }, }; use raphtory_storage::core_ops::CoreGraphOps; use std::{ @@ -25,26 +24,22 @@ use std::{ }; use storage::persist::strategy::PersistenceStrategy; -#[cfg(feature = "io")] fn default_sf10_graph_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../ldbc/data/social_network-sf10-CsvComposite-LongDateFormatter/graph") } -#[cfg(feature = "io")] fn default_sf10_parquet_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join( "../../ldbc/data/social_network-sf10-CsvComposite-LongDateFormatter/parquet/data0/graph0", ) } -#[cfg(feature = "io")] fn default_sf1_graph_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../ldbc/data/social_network-sf1-CsvComposite-LongDateFormatter/graph") } -#[cfg(feature = "io")] fn default_sf1_parquet_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join( "../../ldbc/data/social_network-sf1-CsvComposite-LongDateFormatter/parquet/data0/graph0", @@ -83,7 +78,6 @@ fn test_materialize() { assert_graph_equal_timestamps(&expected, &g); } -#[cfg(feature = "io")] #[test] #[ignore = "requires a locally persisted SNB SF1 graph produced by ldbc/load_snb_sf10.py"] fn test_get_materialize_snb_sf1_time() { @@ -127,7 +121,6 @@ fn test_get_materialize_snb_sf1_time() { remove_dir_all_ignore_not_found(&rb_materialize_graph_path).unwrap(); } -#[cfg(feature = "io")] #[test] #[ignore = "requires a locally persisted SNB SF1 graph produced by ldbc/load_snb_sf10.py"] fn test_materialize_filtered_sf1_matches() { @@ -210,7 +203,6 @@ fn test_materialize_filtered_sf1_matches() { remove_dir_all_ignore_not_found(&rb_materialize_graph_path).unwrap(); } -#[cfg(feature = "io")] fn get_materialize_time(graph_path: &Path, materialize_graph_path: &Path) -> Duration { remove_dir_all_ignore_not_found(&materialize_graph_path).unwrap(); fs::create_dir_all(&materialize_graph_path).unwrap(); @@ -248,7 +240,6 @@ fn get_materialize_time(graph_path: &Path, materialize_graph_path: &Path) -> Dur materialize_elapsed } -#[cfg(feature = "io")] fn get_parquet_decode_time( graph_path: &Path, parquet_path: &Path, @@ -319,7 +310,6 @@ const EDGES_T_PARQUET_DIR: &str = "edges_t"; const EDGES_D_PARQUET_DIR: &str = "edges_d"; const EDGES_C_PARQUET_DIR: &str = "edges_c"; -#[cfg(feature = "io")] fn parquet_prop_columns(path: &Path, exclude: &[&str]) -> Vec { get_parquet_file_paths(path) .unwrap() @@ -338,7 +328,6 @@ fn parquet_prop_columns(path: &Path, exclude: &[&str]) -> Vec { .unwrap_or_default() } -#[cfg(feature = "io")] fn get_parquet_encode_time(graph_path: &Path, parquet_graph_path: &Path) -> Duration { remove_dir_all_ignore_not_found(&parquet_graph_path).unwrap(); fs::create_dir_all(&parquet_graph_path).unwrap(); @@ -368,7 +357,6 @@ fn get_parquet_encode_time(graph_path: &Path, parquet_graph_path: &Path) -> Dura parquet_dump_elapsed } -#[cfg(feature = "io")] fn get_parquet_df_loader_time( graph_path: &Path, parquet_path: &Path, @@ -656,7 +644,6 @@ fn get_parquet_df_loader_time( parquet_load_elapsed } -#[cfg(feature = "io")] #[test] #[ignore = "requires locally persisted SNB SF10 graphs and parquet export"] fn test_current() { diff --git a/raphtory-test-utils/tests/tests_node_type_filtered_subgraph.rs b/raphtory-test-utils/tests/tests_node_type_filtered_subgraph.rs new file mode 100644 index 0000000000..3c7265e9f2 --- /dev/null +++ b/raphtory-test-utils/tests/tests_node_type_filtered_subgraph.rs @@ -0,0 +1,674 @@ +use proptest::{arbitrary::any, proptest}; +use raphtory::{ + db::{ + api::view::Filter, + graph::{ + graph::assert_graph_equal, + views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }, + }, + }, + prelude::*, +}; +use raphtory_storage::mutation::addition_ops::InternalAdditionOps; +use raphtory_test_utils::test_utils::{ + build_graph, build_graph_strat, make_node_types, GraphFixture, +}; +use serde_json::json; +use std::ops::Range; + +#[test] +fn test_type_filtered_subgraph() { + let graph = Graph::new(); + let edges = vec![ + (1, "A", "B", vec![("p1", 1u64)], None), + (2, "B", "C", vec![("p1", 2u64)], None), + (3, "C", "D", vec![("p1", 3u64)], None), + (4, "D", "E", vec![("p1", 4u64)], None), + ]; + + for (id, src, dst, props, layer) in &edges { + graph + .add_edge(*id, src, dst, props.clone(), *layer) + .unwrap(); + } + + let nodes = vec![ + (1, "A", vec![("p1", 1u64)], Some("water_tribe")), + (2, "B", vec![("p1", 2u64)], Some("water_tribe")), + (3, "C", vec![("p1", 1u64)], Some("fire_nation")), + (4, "D", vec![("p1", 1u64)], Some("air_nomads")), + ]; + + for (id, name, props, layer) in &nodes { + graph + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); + } + + let filtered_graph = graph + .subgraph_node_types(vec!["fire_nation", "air_nomads"]) + .window(1, 5); + + assert_eq!(filtered_graph.nodes(), vec!["C", "D"]); + + assert_eq!( + filtered_graph + .filter(NodeFilter.property("p1").eq(1u64)) + .unwrap() + .nodes(), + vec!["C", "D"] + ); + + assert!(filtered_graph + .filter(EdgeFilter.property("p1").eq(1u64)) + .unwrap() + .edges() + .is_empty()) +} + +#[test] +fn materialize_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), node_types in make_node_types())| { + let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); + let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); + }) +} + +#[test] +fn materialize_type_window_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), node_types in make_node_types())| { + let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); + let gvw = g.window(w.start, w.end); + let gmw = gvw.materialize().unwrap(); + assert_graph_equal(&gvw, &gmw); + }) +} + +#[test] +fn materialize_type_window_prop_test_failure() { + let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{"9":{"props":{"t_props":[[1,[]]],"c_props":[]},"node_type":"one"},"8":{"props":{"t_props":[[1,[]]],"c_props":[]},"node_type":"one"}},"edges":[[[8,8,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); + let w = 1..2; + let node_types = ["one"]; + let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); + let gvw = g.window(w.start, w.end); + assert_eq!(gvw.node("8").unwrap().out_degree(), 0); // edge is not in the window + let gmw = gvw.materialize().unwrap(); + assert_graph_equal(&gvw, &gmw); +} + +#[test] +fn materialize_window_type_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), node_types in make_node_types())| { + let g = Graph::from(build_graph(&graph_f)); + let gvw = g.window(w.start, w.end).subgraph_node_types(node_types); + let gmw = gvw.materialize().unwrap(); + assert_graph_equal(&gvw, &gmw); + }) +} + +#[test] +fn node_removed_via_edge_removal() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.node(1).unwrap().set_node_type("test").unwrap(); + let expected = Graph::new(); + expected.resolve_layer(None).unwrap(); + assert_graph_equal(&g.subgraph_node_types(["test"]), &expected); +} + +#[test] +fn node_removed_via_edge_removal_window() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.node(0).unwrap().set_node_type("two").unwrap(); + let gw = g.window(0, 1); + let expected = Graph::new(); + expected.resolve_layer(None).unwrap(); + let sg = gw.subgraph_node_types(["_default"]); + assert!(!sg.has_node(0)); + assert!(!sg.has_node(1)); + assert_graph_equal(&sg, &expected); + assert_graph_equal(&sg, &sg.materialize().unwrap()) +} +mod test_filters_node_type_filtered_subgraph { + use raphtory::{ + db::{ + api::{state::ops::filter::NodeTypeFilterOp, view::StaticGraphViewOps}, + graph::views::{ + filter::node_filtered_graph::NodeFilteredGraph, layer_graph::LayeredGraph, + window_graph::WindowedGraph, + }, + }, + prelude::{GraphViewOps, LayerOps, NodeViewOps, TimeOps}, + }; + use raphtory_test_utils::assertions::GraphTransformer; + use std::ops::Range; + + fn get_all_node_types(graph: &G) -> Vec { + graph + .nodes() + .node_type() + .into_iter() + .flat_map(|(_, node_type)| node_type) + .map(|s| s.to_string()) + .collect() + } + + struct NodeTypeGraphTransformer(Option>); + + impl GraphTransformer for NodeTypeGraphTransformer { + type Return = NodeFilteredGraph; + fn apply(&self, graph: G) -> Self::Return { + let node_types: Vec = + self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); + graph.subgraph_node_types(node_types) + } + } + + struct WindowedNodeTypeGraphTransformer(Option>, Range); + + impl GraphTransformer for WindowedNodeTypeGraphTransformer { + type Return = WindowedGraph>; + fn apply(&self, graph: G) -> Self::Return { + let node_types: Vec = + self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); + graph + .subgraph_node_types(node_types) + .window(self.1.start, self.1.end) + } + } + + struct LayeredNodeTypeGraphTransformer(Option>, Vec); + + impl GraphTransformer for LayeredNodeTypeGraphTransformer { + type Return = LayeredGraph>; + fn apply(&self, graph: G) -> Self::Return { + let node_types: Vec = + self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); + graph + .subgraph_node_types(node_types) + .layers(self.1.clone()) + .unwrap() + } + } + + struct LayeredWindowedNodeTypeGraphTransformer(Option>, Range, Vec); + + impl GraphTransformer for LayeredWindowedNodeTypeGraphTransformer { + type Return = + WindowedGraph>>; + fn apply(&self, graph: G) -> Self::Return { + let node_types: Vec = + self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); + graph + .subgraph_node_types(node_types) + .layers(self.2.clone()) + .unwrap() + .window(self.1.start, self.1.end) + } + } + + mod test_nodes_filters_node_type_filtered_subgraph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::property_filter::ops::PropertyFilterOps, + }, + prelude::AdditionOps, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + fn init_graph(graph: G) -> G { + let nodes = vec![ + (6, "N1", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + (7, "N1", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (6, "N2", vec![("p1", Prop::U64(1u64))], Some("water_tribe")), + (7, "N2", vec![("p1", Prop::U64(2u64))], Some("water_tribe")), + (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (5, "N5", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (6, "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (6, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (3, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + (4, "N8", vec![("p1", Prop::U64(2u64))], Some("fire_nation")), + ]; + + // Add nodes to the graph + for (id, name, props, layer) in &nodes { + graph + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); + } + + graph + } + + use crate::test_filters_node_type_filtered_subgraph::{ + NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, + }; + use raphtory::{ + db::graph::views::filter::model::PropertyFilterFactory, prelude::NodeFilter, + }; + use raphtory_test_utils::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, + TestVariants, + }; + #[test] + fn test_nodes_filters() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + NodeTypeGraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + NodeTypeGraphTransformer(None), + filter, + &expected_results, + TestVariants::All, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N4", "N7"]; + assert_filter_nodes_results( + init_graph, + NodeTypeGraphTransformer(node_types.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + init_graph, + NodeTypeGraphTransformer(node_types), + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_nodes_filters_w() { + // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_filters_pg_w() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + } + + mod test_edges_filters_node_type_filtered_subgraph { + use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::filter::model::property_filter::ops::PropertyFilterOps, + }, + prelude::{AdditionOps, NO_PROPS}, + }; + use raphtory_api::core::entities::properties::prop::Prop; + + fn init_graph(graph: G) -> G { + let edges = vec![ + ( + 6, + "N1", + "N2", + vec![("p1", Prop::U64(2u64))], + Some("fire_nation"), + ), + (7, "N1", "N2", vec![("p1", Prop::U64(1u64))], None), + ( + 6, + "N2", + "N3", + vec![("p1", Prop::U64(1u64))], + Some("water_tribe"), + ), + ( + 7, + "N2", + "N3", + vec![("p1", Prop::U64(2u64))], + Some("water_tribe"), + ), + ( + 8, + "N3", + "N4", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + (9, "N4", "N5", vec![("p1", Prop::U64(1u64))], None), + ( + 5, + "N5", + "N6", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + (6, "N5", "N6", vec![("p1", Prop::U64(2u64))], None), + ( + 5, + "N6", + "N7", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + ( + 6, + "N6", + "N7", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + ( + 3, + "N7", + "N8", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + (5, "N7", "N8", vec![("p1", Prop::U64(1u64))], None), + ( + 3, + "N8", + "N1", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + ( + 4, + "N8", + "N1", + vec![("p1", Prop::U64(2u64))], + Some("water_tribe"), + ), + ]; + + for (id, src, dst, props, layer) in &edges { + graph + .add_edge(*id, src, dst, props.clone(), *layer) + .unwrap(); + } + + let nodes = vec![ + (6, "N1", NO_PROPS, Some("air_nomad")), + (6, "N2", NO_PROPS, Some("water_tribe")), + (8, "N3", NO_PROPS, Some("air_nomad")), + (9, "N4", NO_PROPS, Some("air_nomad")), + (5, "N5", NO_PROPS, Some("air_nomad")), + (5, "N6", NO_PROPS, Some("fire_nation")), + (3, "N7", NO_PROPS, Some("air_nomad")), + (4, "N8", NO_PROPS, Some("fire_nation")), + ]; + + for (id, name, props, layer) in &nodes { + graph + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); + } + + graph + } + + use crate::test_filters_node_type_filtered_subgraph::{ + LayeredNodeTypeGraphTransformer, LayeredWindowedNodeTypeGraphTransformer, + NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, + }; + use raphtory::{ + db::graph::views::filter::model::PropertyFilterFactory, prelude::EdgeFilter, + }; + + use raphtory_test_utils::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestVariants, + }; + #[test] + fn test_edges_filters() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + NodeTypeGraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + NodeTypeGraphTransformer(None), + filter, + &expected_results, + TestVariants::All, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N4->N5"]; + assert_filter_edges_results( + init_graph, + NodeTypeGraphTransformer(node_types.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + NodeTypeGraphTransformer(node_types.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + + let layers = vec!["fire_nation".to_string()]; + let expected_results = vec!["N3->N4"]; + assert_filter_edges_results( + init_graph, + LayeredNodeTypeGraphTransformer(node_types.clone(), layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredNodeTypeGraphTransformer(node_types.clone(), layers), + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_filters_w() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4"]; + assert_filter_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let layers = vec!["fire_nation".to_string()]; + let expected_results = vec!["N3->N4"]; + assert_filter_edges_results( + init_graph, + LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_w() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let node_types: Option> = + Some(vec!["air_nomad".into(), "water_tribe".into()]); + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4"]; + assert_filter_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let layers = vec!["fire_nation".to_string()]; + let expected_results = vec!["N3->N4"]; + assert_filter_edges_results( + init_graph, + LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers.clone()), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + } +} diff --git a/raphtory-test-utils/tests/time_tests.rs b/raphtory-test-utils/tests/time_tests.rs new file mode 100644 index 0000000000..7ea852659d --- /dev/null +++ b/raphtory-test-utils/tests/time_tests.rs @@ -0,0 +1,331 @@ +use itertools::Itertools; +use raphtory::{ + db::{ + api::{mutation::AdditionOps, view::WindowSet}, + graph::{ + graph::{assert_graph_equal, Graph}, + views::deletion_graph::PersistentGraph, + }, + }, + prelude::{DeletionOps, GraphViewOps, LayerOps, TimeOps, NO_PROPS}, +}; +use raphtory_api::core::{ + storage::timeindex::AsTime, + utils::time::{ParseTimeError, TryIntoTime}, +}; +use raphtory_test_utils::test_storage; + +// start inclusive, end exclusive +fn graph_with_timeline(start: i64, end: i64) -> Graph { + let g = Graph::new(); + g.add_edge(start, 0, 1, NO_PROPS, None).unwrap(); + g.add_edge(end - 1, 0, 1, NO_PROPS, None).unwrap(); + g +} + +fn assert_bounds<'graph, G>(windows: WindowSet<'graph, G>, expected: &[(Option, Option)]) +where + G: GraphViewOps<'graph>, +{ + let window_bounds = windows + .map(|w| (w.start().map(|t| t.t()), w.end().map(|t| t.t()))) + .collect_vec(); + assert_eq!(window_bounds, expected) +} + +#[test] +fn snapshot() { + let graph = PersistentGraph::new(); + graph.add_edge(3, 0, 1, [("a", "a")], None).unwrap(); + graph.add_edge(4, 0, 2, [("b", "b")], None).unwrap(); + graph.delete_edge(5, 0, 1, None).unwrap(); + + for time in 2..7 { + assert_graph_equal(&graph.at(time), &graph.snapshot_at(time)); + } + assert_graph_equal(&graph.latest(), &graph.snapshot_latest()); + + let graph = graph.event_graph(); + + for time in 2..7 { + assert_graph_equal(&graph.before(time + 1), &graph.snapshot_at(time)); + } + assert_graph_equal(&graph, &graph.snapshot_latest()); +} + +#[test] +fn rolling() { + let graph = graph_with_timeline(1, 7); + test_storage!(&graph, |graph| { + let windows = graph.rolling(2, None).unwrap(); + let expected = vec![(Some(1), Some(3)), (Some(3), Some(5)), (Some(5), Some(7))]; + assert_bounds(windows, &expected); + }); + + let graph = graph_with_timeline(1, 6); + test_storage!(&graph, |graph| { + let windows = graph.rolling(3, Some(2)).unwrap(); + let expected = vec![(Some(0), Some(3)), (Some(2), Some(5)), (Some(4), Some(7))]; + assert_bounds(windows, &expected); + }); + + let graph = graph_with_timeline(0, 9); + test_storage!(&graph, |graph| { + let windows = graph.window(1, 6).rolling(3, Some(2)).unwrap(); + assert_bounds( + windows, + &[(Some(1), Some(3)), (Some(2), Some(5)), (Some(4), Some(6))], + ); + }); +} + +#[test] +fn rolling_layered() { + let graph = Graph::new(); + graph.add_edge(1, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(5, 0, 1, NO_PROPS, Some("1")).unwrap(); + + graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); + graph.add_edge(6, 0, 1, NO_PROPS, Some("2")).unwrap(); + + test_storage!(&graph, |graph| { + let windows = graph.rolling(2, None).unwrap(); + let expected = vec![(Some(1), Some(3)), (Some(3), Some(5)), (Some(5), Some(7))]; + assert_bounds(windows, &expected); + + let windows = graph.layers("1").unwrap().rolling(2, None).unwrap(); + assert_bounds(windows, &expected); + + let windows = graph.layers("2").unwrap().rolling(2, None).unwrap(); + assert_bounds(windows, &expected); + }); + + let graph = Graph::new(); + graph.add_edge(1, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(3, 0, 1, NO_PROPS, Some("1")).unwrap(); + + graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); + graph.add_edge(5, 0, 1, NO_PROPS, Some("2")).unwrap(); + test_storage!(&graph, |graph| { + let windows = graph.rolling(3, Some(2)).unwrap(); + let expected = vec![(Some(0), Some(3)), (Some(2), Some(5)), (Some(4), Some(7))]; + assert_bounds(windows, &expected); + + let windows = graph.layers("1").unwrap().rolling(3, Some(2)).unwrap(); + assert_bounds(windows, &expected); + + let windows = graph.layers("2").unwrap().rolling(3, Some(2)).unwrap(); + assert_bounds(windows, &expected); + }); + + let graph = Graph::new(); + graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); + graph.add_edge(5, 0, 1, NO_PROPS, Some("1")).unwrap(); + + graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); + graph.add_edge(8, 0, 1, NO_PROPS, Some("2")).unwrap(); + test_storage!(&graph, |graph| { + let windows = graph.window(1, 6).rolling(3, Some(2)).unwrap(); + let expected = [(Some(1), Some(3)), (Some(2), Some(5)), (Some(4), Some(6))]; + assert_bounds(windows, &expected); + + let windows = graph + .layers("1") + .unwrap() + .window(1, 6) + .rolling(3, Some(2)) + .unwrap(); + assert_bounds(windows, &expected); + + let windows = graph + .layers("2") + .unwrap() + .window(1, 6) + .rolling(3, Some(2)) + .unwrap(); + assert_bounds(windows, &expected); + }); +} + +#[test] +fn expanding() { + let graph = graph_with_timeline(1, 7); + test_storage!(&graph, |graph| { + let windows = graph.expanding(2).unwrap(); + let expected = vec![(None, Some(3)), (None, Some(5)), (None, Some(7))]; + assert_bounds(windows, &expected); + }); + + let graph = graph_with_timeline(1, 6); + test_storage!(&graph, |graph| { + let windows = graph.expanding(2).unwrap(); + let expected = vec![(None, Some(3)), (None, Some(5)), (None, Some(7))]; + assert_bounds(windows, &expected); + }); + + let graph = graph_with_timeline(0, 9); + test_storage!(&graph, |graph| { + let windows = graph.window(1, 6).expanding(2).unwrap(); + assert_bounds( + windows, + &[(Some(1), Some(3)), (Some(1), Some(5)), (Some(1), Some(6))], + ); + }); +} + +#[test] +fn rolling_dates() { + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + test_storage!(&graph, |graph| { + let windows = graph.rolling("1 day", None).unwrap(); + let expected = vec![ + ( + "2020-06-06 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ( + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 + "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ]; + assert_bounds(windows, &expected); + }); + + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-08 00:00:00".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + test_storage!(&graph, |graph| { + let windows = graph.rolling("1 day", None).unwrap(); + let expected = vec![ + ( + "2020-06-06 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ( + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-07 + "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ]; + assert_bounds(windows, &expected); + }); + + // TODO: turn this back on if we bring bach epoch alignment for unwindowed graphs + // let start = "2020-06-05 23:59:59.999".into_time().unwrap(); + // let end = "2020-06-07 00:00:00.000".into_time().unwrap(); + // let g = graph_with_timeline(start, end); + // let windows = g.rolling("1 day", None).unwrap(); + // let expected = vec![ + // ( + // "2020-06-05 00:00:00".into_time().unwrap(), // entire 2020-06-06 + // "2020-06-06 00:00:00".into_time().unwrap(), + // ), + // ( + // "2020-06-06 00:00:00".into_time().unwrap(), // entire 2020-06-07 + // "2020-06-07 00:00:00".into_time().unwrap(), + // ), + // ]; + // assert_bounds(windows, expected); +} + +#[test] +fn test_errors() { + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + match graph.rolling("1 day", Some("0 days")) { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert_eq!(e, ParseTimeError::ZeroSizeStep) + } + } + match graph.rolling(1, Some(0)) { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert_eq!(e, ParseTimeError::ZeroSizeStep) + } + } + match graph.expanding("0 day") { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert_eq!(e, ParseTimeError::ZeroSizeStep) + } + } + match graph.expanding(0) { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert_eq!(e, ParseTimeError::ZeroSizeStep) + } + } + + match graph.expanding("0fead day") { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert!(matches!(e, ParseTimeError::ParseInt { .. })) + } + } + + match graph.expanding("0 dadasasdy") { + Ok(_) => { + panic!("Expected error, but got Ok") + } + Err(e) => { + assert!(matches!(e, ParseTimeError::InvalidUnit { .. })) + } + } + + assert_eq!( + graph.rolling("1 day", Some("1000 days")).unwrap().count(), + 0 + ) +} + +#[test] +fn expanding_dates() { + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + test_storage!(&graph, |graph| { + let windows = graph.expanding("1 day").unwrap(); + let expected = vec![ + ( + None, + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ( + None, + "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ]; + assert_bounds(windows, &expected); + }); + + let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); + let end = "2020-06-08 00:00:00".try_into_time().unwrap().t(); + let graph = graph_with_timeline(start, end); + test_storage!(&graph, |graph| { + let windows = graph.expanding("1 day").unwrap(); + let expected = vec![ + ( + None, + "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ( + None, + "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), + ), + ]; + assert_bounds(windows, &expected); + }); +} diff --git a/raphtory-test-utils/tests/valid_graph.rs b/raphtory-test-utils/tests/valid_graph.rs new file mode 100644 index 0000000000..f759a6995d --- /dev/null +++ b/raphtory-test-utils/tests/valid_graph.rs @@ -0,0 +1,379 @@ +use itertools::Itertools; +use proptest::{arbitrary::any, proptest}; +use raphtory::{ + db::graph::{ + graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, + views::deletion_graph::PersistentGraph, + }, + errors::GraphError, + prelude::*, +}; +use raphtory_api::core::storage::timeindex::AsTime; +use raphtory_storage::mutation::addition_ops::InternalAdditionOps; +use raphtory_test_utils::test_utils::{build_graph, build_graph_strat}; +use std::ops::Range; + +#[test] +fn test_valid_graph_persistent() -> Result<(), GraphError> { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None)?; + g.delete_edge(10, 0, 1, None)?; + + assert_eq!( + g.window(0, 2).valid().edges().id().collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); + assert!(g.valid().edges().is_empty()); + Ok(()) +} + +#[test] +fn test_valid_graph_events() -> Result<(), GraphError> { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None)?; + g.delete_edge(10, 0, 1, None)?; + + assert_eq!( + g.window(0, 2).valid().edges().id().collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); + assert!(g.window(1, 20).valid().edges().is_empty()); // only deletion event in window, not valid + assert_eq!( + g.valid().edges().id().collect_vec(), + [(GID::U64(0), GID::U64(1))] + ); + Ok(()) +} + +#[test] +fn materialize_prop_test_persistent() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { + let g = PersistentGraph::from(build_graph(&graph_f)).valid(); + let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); + }) +} + +#[test] +fn test_explode_layers() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, Some("a")).unwrap(); + g.delete_edge(0, 0, 1, Some("b")).unwrap(); + let gv = g.valid(); + let edge = gv.edge(0, 1).unwrap(); + let exploded = edge.explode_layers(); + let layers = exploded + .layer_name() + .collect::, _>>() + .unwrap(); + assert_eq!(layers, ["a"]); +} + +#[test] +fn materialize_prop_test_events() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { + let g = Graph::from(build_graph(&graph_f)).valid(); + let gm = g.materialize().unwrap(); + assert_graph_equal(&g, &gm); + }) +} + +#[test] +fn test_materialize_self_loop() { + let g = Graph::new(); + g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); + let gm = g.valid().materialize().unwrap(); + assert_graph_equal(&g, &gm); +} + +#[test] +fn test_single_deleted_edge_events() { + let g = Graph::new(); + g.delete_edge(0, 0, 0, Some("a")).unwrap(); + let gv = g.valid(); + assert_eq!(gv.count_nodes(), 0); + assert_eq!(gv.count_edges(), 0); + assert_eq!(gv.count_temporal_edges(), 0); + + assert_eq!(gv.valid_layers("a").count_nodes(), 0); + + let expected = Graph::new(); + expected.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gv, &expected); + let gvm = gv.materialize().unwrap(); + assert_graph_equal(&gv, &gvm); +} + +#[test] +fn test_single_deleted_edge_persistent() { + let g = PersistentGraph::new(); + g.delete_edge(0, 0, 0, Some("a")).unwrap(); + let gv = g.valid(); + assert_eq!(gv.count_nodes(), 0); + assert_eq!(gv.count_edges(), 0); + assert_eq!(gv.count_temporal_edges(), 0); + + let expected = PersistentGraph::new(); + expected.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gv, &expected); + let gvm = gv.materialize().unwrap(); + assert_graph_equal(&gv, &gvm); +} + +#[test] +fn materialize_valid_window_persistent_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = PersistentGraph::from(build_graph(&graph_f)); + let gvw = g.valid().window(w.start, w.end); + let gmw = gvw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gvw, &gmw); + }) +} + +#[test] +fn test_deletions_in_window_but_edge_valid() { + let g = PersistentGraph::new(); + g.delete_edge(0, 0, 0, None).unwrap(); + g.delete_edge(0, 0, 1, None).unwrap(); + g.add_edge(5, 0, 1, NO_PROPS, None).unwrap(); + let gvw = g.valid().window(-1, 1); + assert_eq!(gvw.node(0).unwrap().out_degree(), 1); +} + +#[test] +fn materialize_valid_window_events_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = Graph::from(build_graph(&graph_f)); + let gvw = g.valid().window(w.start, w.end); + let gmw = gvw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gvw, &gmw); + }) +} + +#[test] +fn materialize_window_valid_persistent_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = PersistentGraph::from(build_graph(&graph_f)); + let gvw = g.window(w.start, w.end).valid(); + let gmw = gvw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gvw, &gmw); + }) +} + +#[test] +fn materialize_window_valid_events_prop_test() { + proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { + let g = Graph::from(build_graph(&graph_f)); + let gvw = g.window(w.start, w.end).valid(); + let gmw = gvw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gvw, &gmw); + }) +} + +#[test] +fn broken_earliest_time() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.delete_edge(1, 0, 1, None).unwrap(); + let gv = g.valid(); + let expected = PersistentGraph::new(); + expected.resolve_layer(None).unwrap(); + assert_graph_equal(&gv, &expected); + let gm = gv.materialize().unwrap(); + assert_graph_equal(&gv, &gm); +} + +#[test] +fn broken_earliest_time2() { + let g = PersistentGraph::new(); + + g.add_edge(10, 0, 0, NO_PROPS, None).unwrap(); + g.delete_edge(0, 0, 0, None).unwrap(); + + let w = 1..11; + + let gv = g.valid(); + assert_eq!(gv.node(0).unwrap().earliest_time().map(|t| t.t()), Some(0)); + + let gvw = gv.window(w.start, w.end); + assert_eq!( + gvw.node(0).unwrap().earliest_time().map(|t| t.t()), + Some(10) + ); + + assert_eq!(gvw.node(0).unwrap().history(), [10]); + + let gvwm = gvw.materialize().unwrap(); + assert_eq!( + gvwm.node(0).unwrap().earliest_time().map(|t| t.t()), + Some(10) + ); +} + +#[test] +fn broken_earliest_time3() { + let g = PersistentGraph::new(); + g.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); + g.delete_edge(100, 0, 0, None).unwrap(); + let gvw = g.valid().window(2, 20); + assert_eq!( + gvw.node(0).unwrap().earliest_time().map(|t| t.t()), + Some(10) + ); + let gvwm = gvw.materialize().unwrap(); + println!("{:?}", gvwm); + assert_eq!( + gvwm.node(0).unwrap().earliest_time().map(|t| t.t()), + Some(10) + ); +} + +#[test] +fn missing_temporal_edge() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); + g.add_edge(7658643179498972033, 0, 0, NO_PROPS, None) + .unwrap(); + g.add_edge(781965068308597440, 0, 0, NO_PROPS, Some("b")) + .unwrap(); + let gv = g.valid(); + assert_graph_equal(&gv, &g); + let gvm = gv.materialize().unwrap(); + println!("{:?}", gvm); + assert_graph_equal(&gv, &gvm); +} + +#[test] +fn wrong_temporal_edge_count() { + let g = PersistentGraph::new(); + g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.add_edge(1, 0, 1, NO_PROPS, None).unwrap(); + g.add_edge(2, 1, 0, NO_PROPS, None).unwrap(); + g.add_edge(3, 1, 0, NO_PROPS, Some("b")).unwrap(); + let gw = g.valid().window(0, 9); + let gwm = gw.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gw, &gwm); // PersistentGraph ignores the earliest time's event id +} + +#[test] +fn mismatched_edge_properties() { + let g = PersistentGraph::new(); + g.add_edge(2, 1, 0, [("test", 1)], Some("b")).unwrap(); + g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); + g.add_edge(0, 0, 1, NO_PROPS, None) + .unwrap() + .add_metadata([("const_test", 2)], None) + .unwrap(); + + let gw = g.valid().window(-1, 1); + assert_eq!( + gw.edge(0, 1).unwrap().metadata().get("const_test").unwrap(), + Prop::map([("_default", 2)]) + ); + assert_eq!( + gw.edge(0, 1) + .unwrap() + .default_layer() + .metadata() + .get("const_test") + .unwrap(), + 2.into() + ); + assert_graph_equal(&gw, &gw.materialize().unwrap()); +} + +#[test] +fn node_earliest_time() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + g.add_edge(0, 0, 2, NO_PROPS, None).unwrap(); + g.add_edge(2, 0, 2, NO_PROPS, None).unwrap(); + g.delete_edge(-10, 0, 1, None).unwrap(); + + let gv = g.valid().window(-1, 10); + let gvm = gv.materialize().unwrap(); + assert_graph_equal(&gv, &gvm); + assert_eq!(gv.node(0).unwrap().earliest_time().map(|t| t.t()), Some(0)); +} + +#[test] +fn broken_degree() { + let g = PersistentGraph::new(); + g.add_edge(0, 5, 4, NO_PROPS, None).unwrap(); + g.add_edge(0, 4, 9, NO_PROPS, None).unwrap(); + g.add_edge(0, 4, 6, NO_PROPS, None).unwrap(); + g.add_edge(0, 4, 6, NO_PROPS, Some("b")).unwrap(); + g.add_edge(1, 4, 9, NO_PROPS, Some("a")).unwrap(); + g.add_edge(2, 5, 4, NO_PROPS, Some("a")).unwrap(); + g.delete_edge(10, 5, 4, None).unwrap(); + + let gv = g.valid().window(0, 20); + assert!(!gv.default_layer().has_edge(5, 4)); + assert_eq!(gv.edge(5, 4).unwrap().latest_time().map(|t| t.t()), Some(2)); + assert_eq!(gv.earliest_time().map(|t| t.t()), Some(0)); + assert_eq!(gv.latest_time().map(|t| t.t()), Some(2)); + assert_eq!(gv.node(6).unwrap().latest_time().map(|t| t.t()), Some(0)); + let expected = PersistentGraph::new(); + expected.add_edge((0, 1), 4, 9, NO_PROPS, None).unwrap(); + expected.add_edge((0, 2), 4, 6, NO_PROPS, None).unwrap(); + expected + .add_edge((0, 3), 4, 6, NO_PROPS, Some("b")) + .unwrap(); + expected + .add_edge((1, 4), 4, 9, NO_PROPS, Some("a")) + .unwrap(); + expected + .add_edge((2, 5), 5, 4, NO_PROPS, Some("a")) + .unwrap(); + + assert_persistent_materialize_graph_equal(&gv, &expected); // PersistentGraph ignores the earliest time's event id + + let n4 = gv.node(4).unwrap(); + assert_eq!(n4.out_degree(), 2); + assert_eq!(n4.in_degree(), 1); + + assert_persistent_materialize_graph_equal(&gv, &gv.materialize().unwrap()); + // PersistentGraph ignores the earliest time's event id +} + +#[test] +fn weird_empty_graph() { + let g = PersistentGraph::new(); + g.add_edge(10, 2, 1, NO_PROPS, Some("a")).unwrap(); + g.delete_edge(5, 2, 1, None).unwrap(); + g.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); + let gvw = g.valid().window(0, 5); + assert_eq!(gvw.count_nodes(), 0); + let expected = PersistentGraph::new(); + expected.resolve_layer(None).unwrap(); + expected.resolve_layer(Some("a")).unwrap(); + assert_graph_equal(&gvw, &expected); + let gvwm = gvw.materialize().unwrap(); + assert_graph_equal(&gvw, &gvwm); +} + +#[test] +fn mismatched_node_earliest_time_again() { + let g = PersistentGraph::new(); + g.add_node(-2925244660385668056, 1, NO_PROPS, None, None) + .unwrap(); + g.add_edge(1116793271088085151, 2, 1, NO_PROPS, Some("a")) + .unwrap(); + g.add_edge(0, 9, 1, NO_PROPS, None).unwrap(); + g.delete_edge(7891470373966857988, 9, 1, None).unwrap(); + g.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); + + let gv = g.window(-2925244660385668055, 7060945172792084486).valid(); + assert_persistent_materialize_graph_equal(&gv, &gv.materialize().unwrap()); +} + +#[test] +fn test_single_edge() { + let g = PersistentGraph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + let gwv = g.window(10, 11).valid(); + let gm = gwv.materialize().unwrap(); + assert_persistent_materialize_graph_equal(&gwv, &gm); +} diff --git a/raphtory-test-utils/tests/views_test.rs b/raphtory-test-utils/tests/views_test.rs new file mode 100644 index 0000000000..abfa51166a --- /dev/null +++ b/raphtory-test-utils/tests/views_test.rs @@ -0,0 +1,4581 @@ +use itertools::Itertools; +use proptest::{prop_assert, prop_assert_eq, prop_assume, proptest}; +use rand::{prelude::*, rng}; +use raphtory::{ + algorithms::centrality::degree_centrality::degree_centrality, + db::graph::{graph::assert_graph_equal, views::window_graph::WindowedGraph}, + prelude::*, +}; +use raphtory_api::core::{ + entities::GID, + storage::timeindex::AsTime, + utils::{logging::global_info_logger, time::IntoTime}, +}; +use raphtory_test_utils::{test_storage, test_utils::test_graph}; +use rayon::prelude::*; +use std::ops::Range; +use tracing::{error, info}; + +#[test] +fn test_non_restricted_window() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + for n in g.window(0, 1).nodes() { + assert!(g.has_node(n)); + } + + assert_graph_equal(&g.window(0, 1), &g) +} + +#[test] +fn windowed_graph_nodes_degree() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let wg = graph.window(-1, 1); + + let actual = wg + .nodes() + .iter() + .map(|v| (v.id(), v.degree())) + .collect::>(); + + let expected = vec![(GID::U64(1), 2), (GID::U64(2), 1)]; + + assert_eq!(actual, expected); + }); +} + +#[test] +fn windowed_graph_edge() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + for (t, src, dst) in vs { + graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let wg = graph.window(i64::MIN, i64::MAX); + assert_eq!(wg.edge(1, 3).unwrap().src().id(), GID::U64(1)); + assert_eq!(wg.edge(1, 3).unwrap().dst().id(), GID::U64(3)); + }); +} + +#[test] +fn windowed_graph_node_edges() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let wg = graph.window(-1, 1); + + assert_eq!(wg.node(1).unwrap().id(), GID::U64(1)); + }); +} + +#[test] +fn graph_has_node_check_fail() { + let vs: Vec<(i64, u64)> = vec![ + (1, 0), + (-100, 262), + // (327226439, 108748364996394682), + (1, 9135428456135679950), + // (0, 1), + // (2, 2), + ]; + let graph = Graph::new(); + + for (t, v) in &vs { + graph.add_node(*t, *v, NO_PROPS, None, None).unwrap(); + } + + // FIXME: Issue #46: arrow_test(&graph, test) + test_graph(&graph, |graph| { + let wg = graph.window(1, 2); + assert!(!wg.has_node(262)) + }); +} + +#[test] +fn windowed_graph_has_node() { + proptest!(|(mut vs: Vec<(i64, u64)>)| { + global_info_logger(); + prop_assume!(!vs.is_empty()); + + vs.sort_by_key(|v| v.1); // Sorted by node + vs.dedup_by_key(|v| v.1); // Have each node only once to avoid headaches + vs.sort_by_key(|v| v.0); // Sorted by time + + let rand_start_index = rng().random_range(0..vs.len()); + let rand_end_index = rng().random_range(rand_start_index..vs.len()); + + let g = Graph::new(); + + for (t, v) in &vs { + g.add_node(*t, *v, NO_PROPS, None, None) + .map_err(|err| error!("{:?}", err)) + .ok(); + } + + let start = vs.get(rand_start_index).expect("start index in range").0; + let end = vs.get(rand_end_index).expect("end index in range").0; + + let wg = g.window(start, end); + + let rand_test_index: usize = rng().random_range(0..vs.len()); + + let (i, v) = vs.get(rand_test_index).expect("test index in range"); + if (start..end).contains(i) { + prop_assert!(wg.has_node(*v), "Node {:?} was not in window {:?}", (i, v), start..end); + } else { + prop_assert!(!wg.has_node(*v), "Node {:?} was in window {:?}", (i, v), start..end); + } + }); +} + +#[test] +fn windowed_graph_has_edge() { + proptest!(|(mut edges: Vec<(i64, (u64, u64))>)| { + prop_assume!(!edges.is_empty()); + + edges.sort_by_key(|e| e.1); // Sorted by edge + edges.dedup_by_key(|e| e.1); // Have each edge only once to avoid headaches + edges.sort_by_key(|e| e.0); // Sorted by time + + let rand_start_index = rng().random_range(0..edges.len()); + let rand_end_index = rng().random_range(rand_start_index..edges.len()); + + let g = Graph::new(); + + for (t, e) in &edges { + g.add_edge(*t, e.0, e.1, NO_PROPS, None).unwrap(); + } + + let start = edges.get(rand_start_index).expect("start index in range").0; + let end = edges.get(rand_end_index).expect("end index in range").0; + + let wg = g.window(start, end); + + let rand_test_index: usize = rng().random_range(0..edges.len()); + + let (i, e) = edges.get(rand_test_index).expect("test index in range"); + if (start..end).contains(i) { + prop_assert!(wg.has_edge(e.0, e.1), "Edge {:?} was not in window {:?}", (i, e), start..end); + } else { + prop_assert!(!wg.has_edge(e.0, e.1), "Edge {:?} was in window {:?}", (i, e), start..end); + } + }); +} + +#[test] +fn windowed_graph_edge_count() { + proptest!(|(mut edges: Vec<(i64, (u64, u64))>, window: Range)| { + global_info_logger(); + prop_assume!(window.end >= window.start); + + edges.sort_by_key(|e| e.1); // Sorted by edge + edges.dedup_by_key(|e| e.1); // Have each edge only once to avoid headaches + + let true_edge_count = edges.iter().filter(|e| window.contains(&e.0)).count(); + + let g = Graph::new(); + + for (t, e) in &edges { + g.add_edge(*t, e.0, e.1, [("test".to_owned(), Prop::Bool(true))], None) + .unwrap(); + } + + let wg = g.window(window.start, window.end); + if wg.count_edges() != true_edge_count { + info!( + "failed, g.num_edges() = {}, true count = {}", + wg.count_edges(), + true_edge_count + ); + info!("g.edges() = {:?}", wg.edges().iter().collect_vec()); + } + prop_assert_eq!(wg.count_edges(), true_edge_count); + }); +} + +#[test] +fn trivial_window_has_all_edges() { + proptest!(|(edges: Vec<(i64, u64, u64)>)| { + let g = Graph::new(); + edges + .into_par_iter() + .filter(|e| e.0 < i64::MAX) + .for_each(|(t, src, dst)| { + g.add_edge(t, src, dst, [("test".to_owned(), Prop::Bool(true))], None) + .unwrap(); + }); + let w = g.window(i64::MIN, i64::MAX); + prop_assert!(g.edges() + .iter() + .all(|e| w.has_edge(e.src().id(), e.dst().id()))); + }); +} + +#[test] +fn large_node_in_window() { + proptest!(|(dsts: Vec)| { + let dsts: Vec = dsts.into_iter().unique().collect(); + let n = dsts.len(); + let g = Graph::new(); + + for dst in dsts { + let t = 1; + g.add_edge(t, 0, dst, NO_PROPS, None).unwrap(); + } + let w = g.window(i64::MIN, i64::MAX); + prop_assert_eq!(w.count_edges(), n); + }); +} + +#[test] +fn windowed_graph_node_ids() { + let vs = vec![(1, 1, 2), (3, 3, 4), (5, 5, 6), (7, 7, 1)]; + + let args = [(i64::MIN, 8), (i64::MIN, 2), (i64::MIN, 4), (3, 6)]; + + let expected = vec![ + vec![1, 2, 3, 4, 5, 6, 7], + vec![1, 2], + vec![1, 2, 3, 4], + vec![3, 4, 5, 6], + ]; + + let graph = Graph::new(); + + for (t, src, dst) in &vs { + graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + let res: Vec<_> = (0..=3) + .map(|i| { + let wg = graph.window(args[i].0, args[i].1); + let mut e = wg + .nodes() + .id() + .iter_values() + .filter_map(|id| id.to_u64()) + .collect::>(); + e.sort(); + e + }) + .collect_vec(); + + assert_eq!(res, expected); + }); + + let graph = Graph::new(); + for (src, dst, t) in &vs { + graph.add_edge(*src, *dst, *t, NO_PROPS, None).unwrap(); + } + test_storage!(&graph, |graph| { + let res: Vec<_> = (0..=3) + .map(|i| { + let wg = graph.window(args[i].0, args[i].1); + let mut e = wg + .nodes() + .id() + .iter_values() + .filter_map(|id| id.to_u64()) + .collect::>(); + e.sort(); + e + }) + .collect_vec(); + assert_eq!(res, expected); + }); +} + +#[test] +fn windowed_graph_nodes() { + let vs = vec![ + (1, 1, 2), + (2, 1, 3), + (-1, 2, 1), + (0, 1, 1), + (7, 3, 2), + (1, 1, 1), + ]; + + let graph = Graph::new(); + + graph + .add_node( + 0, + 1, + [("type", "wallet".into_prop()), ("cost", 99.5.into_prop())], + None, + None, + ) + .unwrap(); + + graph + .add_node( + -1, + 2, + [("type", "wallet".into_prop()), ("cost", 10.0.into_prop())], + None, + None, + ) + .unwrap(); + + graph + .add_node( + 6, + 3, + [("type", "wallet".into_prop()), ("cost", 76.2.into_prop())], + None, + None, + ) + .unwrap(); + + for (t, src, dst) in &vs { + graph + .add_edge(*t, *src, *dst, [("eprop", "commons")], None) + .unwrap(); + } + test_storage!(&graph, |graph| { + let wg = graph.window(-2, 0); + + let actual = wg + .nodes() + .id() + .iter_values() + .filter_map(|id| id.to_u64()) + .collect::>(); + + let expected = vec![1, 2]; + + assert_eq!(actual, expected); + }); +} + +#[test] +fn test_reference() { + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + + test_storage!(&graph, |graph| { + let mut w = WindowedGraph::new(&graph, Some(0.into_time()), Some(1.into_time())); + assert_eq!(w, graph); + w = WindowedGraph::new(&graph, Some(1.into_time()), Some(2.into_time())); + assert_eq!(w, Graph::new()); + }); +} + +#[test] +fn test_algorithm_on_windowed_graph() { + global_info_logger(); + let graph = Graph::new(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + test_storage!(&graph, |graph| { + let w = graph.window(0, 1); + let _ = degree_centrality(&w); + }); +} + +#[test] +fn test_view_resetting() { + let graph = Graph::new(); + for t in 0..10 { + let t1 = t * 3; + let t2 = t * 3 + 1; + let t3 = t * 3 + 2; + graph.add_edge(t1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(t2, 2, 3, NO_PROPS, None).unwrap(); + graph.add_edge(t3, 3, 1, NO_PROPS, None).unwrap(); + } + + test_storage!(&graph, |graph| { + assert_graph_equal(&graph.before(9).after(2), &graph.window(3, 9)); + let res = graph + .window(3, 9) + .nodes() + .before(6) + .edges() + .window(1, 9) + .earliest_time() + .map(|it| it.map(|t_opt| t_opt.map(|t| t.t())).collect_vec()) + .collect_vec(); + assert_eq!( + res, + [[Some(3), Some(5)], [Some(3), Some(4)], [Some(5), Some(4)]] + ); + }); +} + +#[test] +fn test_entity_history() { + let graph = Graph::new(); + graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(1, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(2, 0, NO_PROPS, None, None).unwrap(); + graph.add_node(3, 0, NO_PROPS, None, None).unwrap(); + graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); + graph.add_edge(4, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(5, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(6, 1, 3, NO_PROPS, None).unwrap(); + graph.add_edge(7, 1, 3, NO_PROPS, None).unwrap(); + + // FIXME: Issue #46 + test_graph(&graph, |graph| { + let e = graph.edge(1, 2).unwrap(); + let v = graph.node(0).unwrap(); + let full_history_1 = vec![0i64, 1, 2, 3]; + + let full_history_2 = vec![4i64, 5, 6, 7]; + + let windowed_history = vec![0i64, 1]; + + assert_eq!(v.history(), full_history_1); + + assert_eq!(v.window(0, 2).history(), windowed_history); + assert_eq!(e.history(), full_history_1); + assert_eq!(e.window(0, 2).history(), windowed_history); + + assert_eq!( + graph.edges().history().collect_vec(), + [full_history_1.clone(), full_history_2.clone()] + ); + assert_eq!( + graph + .nodes() + .in_edges() + .history() + .map(|it| it.collect_vec()) + .collect_vec(), + [vec![], vec![], vec![full_history_1], vec![full_history_2],] + ); + + assert_eq!( + graph + .nodes() + .earliest_time() + .iter_values() + .flatten() + .collect_vec(), + [0, 0, 0, 4,] + ); + + assert_eq!( + graph + .nodes() + .latest_time() + .iter_values() + .flatten() + .collect_vec(), + [3, 7, 3, 7] + ); + + assert_eq!( + graph + .nodes() + .neighbours() + .latest_time() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, it)| it.flatten().collect_vec()) + .collect_vec(), + [vec![], vec![3, 7], vec![7], vec![7],] + ); + + assert_eq!( + graph + .nodes() + .neighbours() + .earliest_time() + .sorted_by_key(|(n, _)| n.id()) + .map(|(_, it)| it.flatten().collect_vec()) + .collect_vec(), + [vec![], vec![0, 4], vec![0], vec![0],] + ); + }); +} + +mod test_filters_window_graph { + + mod test_nodes_filters_window_graph { + use raphtory::{ + db::{api::view::StaticGraphViewOps, graph::views::filter::model::ComposableFilter}, + prelude::{AdditionOps, PropertyAdditionOps}, + }; + use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; + use raphtory_storage::mutation::{ + addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, + }; + use raphtory_test_utils::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, + TestVariants, + }; + + use raphtory::{ + db::{ + api::view::filter_ops::Filter, + graph::views::filter::model::{ + node_filter::{ops::NodeFilterOps, NodeFilter}, + property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, + }, + }, + errors::GraphError, + prelude::{Graph, GraphViewOps, TimeOps}, + }; + use raphtory_test_utils::assertions::WindowGraphTransformer; + + fn init_graph(graph: G) -> G { + let nodes = vec![ + ( + 6, + "N1", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ( + 7, + "N1", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(5i64)), + ("k3", Prop::Bool(false)), + ], + Some("air_nomad"), + ), + ( + 6, + "N2", + vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(6.0f64))], + Some("water_tribe"), + ), + ( + 7, + "N2", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("water_tribe"), + ), + (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + ( + 5, + "N5", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ( + 6, + "N5", + vec![ + ("p1", Prop::U64(2u64)), + ("k2", Prop::Str(ArcStr::from("Pometry"))), + ("k4", Prop::F64(1.0f64)), + ], + Some("air_nomad"), + ), + (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + ( + 6, + "N6", + vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(1.0f64))], + Some("fire_nation"), + ), + ( + 3, + "N7", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("air_nomad"), + ), + (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), + (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), + ( + 4, + "N8", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("fire_nation"), + ), + (2, "N9", vec![("p1", Prop::U64(2u64))], None), + (2, "N10", vec![("q1", Prop::U64(0u64))], None), + (2, "N10", vec![("p1", Prop::U64(3u64))], None), + (2, "N11", vec![("p1", Prop::U64(3u64))], None), + (2, "N11", vec![("q1", Prop::U64(0u64))], None), + (2, "N12", vec![("q1", Prop::U64(0u64))], None), + ( + 3, + "N12", + vec![ + ("p1", Prop::U64(3u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + None, + ), + (2, "N13", vec![("q1", Prop::U64(0u64))], None), + (3, "N13", vec![("p1", Prop::U64(3u64))], None), + (2, "N14", vec![("q1", Prop::U64(0u64))], None), + (2, "N15", vec![], None), + ]; + + // Add nodes to the graph + for (id, name, props, layer) in &nodes { + graph + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); + } + + // Metadata property assignments + let metadata = vec![ + ( + "N1", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(3i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + ), + ("N4", vec![("p1", Prop::U64(2u64))]), + ("N9", vec![("p1", Prop::U64(1u64))]), + ("N10", vec![("p1", Prop::U64(1u64))]), + ("N11", vec![("p1", Prop::U64(1u64))]), + ("N12", vec![("p1", Prop::U64(1u64))]), + ( + "N13", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + ), + ("N14", vec![("p1", Prop::U64(1u64))]), + ("N15", vec![("p1", Prop::U64(1u64))]), + ]; + + // Apply metadata + for (node, props) in metadata { + graph.node(node).unwrap().add_metadata(props).unwrap(); + } + + graph + } + + fn init_graph2< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + let nodes = vec![( + 2, + "N14", + vec![ + ("q1", Prop::U64(0u64)), + ( + "x", + Prop::list(vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)]), + ), + ], + None, + )]; + + // Add nodes to the graph + for (id, name, props, layer) in &nodes { + graph + .add_node(*id, name, props.clone(), *layer, None) + .unwrap(); + } + + graph + } + + #[test] + fn test_nodes_filters_for_node_name_eq() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::name().eq("N2"); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_filters_pg_for_node_name_eq() { + let filter = NodeFilter::name().eq("N2"); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_node_name_ne() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::name().ne("N2"); + let expected_results = vec!["N1", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_node_name_ne() { + let filter = NodeFilter::name().ne("N2"); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N3", "N5", "N6", "N7", "N8", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_node_name_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::name().is_in(vec!["N2"]); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter::name().is_in(vec!["N2", "N5"]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_filters_pg_for_node_name_in() { + let filter = NodeFilter::name().is_in(vec!["N2"]); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter::name().is_in(vec!["N2", "N5"]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_node_name_not_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::name().is_not_in(vec!["N5"]); + let expected_results = vec!["N1", "N2", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_node_name_not_in() { + let filter = NodeFilter::name().is_not_in(vec!["N5"]); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N6", "N7", "N8", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_node_type_eq() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::node_type().eq("fire_nation"); + let expected_results = vec!["N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_node_type_eq() { + let filter = NodeFilter::node_type().eq("fire_nation"); + let expected_results = vec!["N6", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_node_type_ne() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::node_type().ne("fire_nation"); + let expected_results = vec!["N1", "N2", "N3", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_node_type_ne() { + let filter = NodeFilter::node_type().ne("fire_nation"); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N5", "N7", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_node_type_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); + let expected_results = vec!["N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomad"]); + let expected_results = vec!["N1", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_node_type_in() { + let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); + let expected_results = vec!["N6", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomad"]); + let expected_results = vec!["N1", "N3", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_node_type_not_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); + let expected_results = vec!["N1", "N2", "N3", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_node_type_not_in() { + let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N5", "N7", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_property_eq() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").eq(2i64); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k2").eq("Paper_Airplane"); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter.property("k3").eq(true); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter.property("k4").eq(6.0f64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = NodeFilter.property("x").eq(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = vec!["N14"]; + // TODO: List(U64) not supported as disk_graph property + // assert_filter_nodes_results_w!( + // init_graph2, + // filter, + // 1..9, + // expected_results, + // variants = [graph] + // ); + assert_filter_nodes_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + // TODO: Search APIs don't support list yet + // assert_search_nodes_results( + // init_graph, + // WindowGraphTransformer(6..9), + // filter, + // &expected_results, + // TestVariants::EventOnly, + // ); + } + + #[test] + fn test_nodes_filters_pg_for_property_eq() { + let filter = NodeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").eq(2i64); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k2").eq("Paper_Airplane"); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + // TODO: Const properties not supported for disk_graph. + let filter = NodeFilter.property("k3").eq(true); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").eq(6.0f64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("x").eq(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = vec!["N14"]; + // TODO: List(U64) not supported as disk_graph property + // assert_filter_nodes_results_pg_w!( + // init_graph2, + // filter, + // 1..9, + // expected_results, + // variants = [persistent_graph] + // ); + assert_filter_nodes_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + // TODO: Search APIs don't support list yet + // assert_search_nodes_results( + // init_graph, + // WindowGraphTransformer(6..9), + // filter, + // &expected_results, + // vec![TestGraphVariants::PersistentGraph], + // ); + } + + #[test] + fn test_nodes_filters_for_property_ne() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").ne(1u64); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").ne(2i64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k2").ne("Paper_Airplane"); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k3").ne(true); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").ne(6.0f64); + let expected_results = vec!["N2", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_property_ne() { + let filter = NodeFilter.property("p1").ne(1u64); + let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").ne(2i64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k2").ne("Paper_Airplane"); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k3").ne(true); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").ne(6.0f64); + let expected_results = vec!["N12", "N2", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_property_lt() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").lt(3u64); + let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").lt(3i64); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").lt(10.0f64); + let expected_results = vec!["N1", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_property_lt() { + let filter = NodeFilter.property("p1").lt(3u64); + let expected_results = vec!["N1", "N2", "N3", "N5", "N6", "N7", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").lt(3i64); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").lt(10.0f64); + let expected_results = vec!["N1", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_property_le() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N1", "N3", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").le(2i64); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").le(6.0f64); + let expected_results = vec!["N1", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_property_le() { + let filter = NodeFilter.property("p1").le(1u64); + let expected_results = vec!["N1", "N3", "N6", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").le(2i64); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").le(6.0f64); + let expected_results = vec!["N1", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_property_gt() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").gt(2i64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").gt(6.0f64); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("x").gt(Prop::List( + vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)].into(), + )); + let graph = init_graph(Graph::new()); + assert!(matches!( + graph.window(1, 9).filter(filter.clone()).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "x" + )); + assert!(matches!( + graph.persistent_graph().window(1, 9).filter(filter).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "x" + )); + } + + #[test] + fn test_nodes_filters_pg_for_property_gt() { + let filter = NodeFilter.property("p1").gt(1u64); + let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").gt(2i64); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").gt(6.0f64); + let expected_results = vec!["N12", "N2", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_property_ge() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").ge(1u64); + let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").ge(2i64); + let expected_results = vec!["N1", "N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").ge(6.0f64); + let expected_results = vec!["N1", "N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_property_ge() { + let filter = NodeFilter.property("p1").ge(1u64); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N5", "N6", "N7", "N8", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").ge(2i64); + let expected_results = vec!["N1", "N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").ge(6.0f64); + let expected_results = vec!["N1", "N12", "N2", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_property_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").is_in(vec![2u64.into()]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").is_in(vec![2i64.into()]); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter + .property("k2") + .is_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k3").is_in(vec![true.into()]); + let expected_results = vec!["N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").is_in(vec![6.0f64.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_property_in() { + let filter = NodeFilter.property("p1").is_in(vec![2u64.into()]); + let expected_results = vec!["N2", "N5", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").is_in(vec![2i64.into()]); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter + .property("k2") + .is_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + // TODO: Const properties not supported for disk_graph. + let filter = NodeFilter.property("k3").is_in(vec![true.into()]); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").is_in(vec![6.0f64.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_property_not_in() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").is_not_in(vec![1u64.into()]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k1").is_not_in(vec![2i64.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter + .property("k2") + .is_not_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N2", "N5"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k3").is_not_in(vec![true.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k4").is_not_in(vec![6.0f64.into()]); + let expected_results = vec!["N2", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_property_not_in() { + let filter = NodeFilter.property("p1").is_not_in(vec![1u64.into()]); + let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k1").is_not_in(vec![2i64.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter + .property("k2") + .is_not_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k3").is_not_in(vec![true.into()]); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k4").is_not_in(vec![6.0f64.into()]); + let expected_results = vec!["N12", "N2", "N5", "N6", "N7", "N8"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_property_is_some() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("p1").is_some(); + let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(1..2), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(1..2), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(10..12), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(10..12), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_for_property_is_some() { + let filter = NodeFilter.property("p1").is_some(); + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N5", "N6", "N7", "N8", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(1..2), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(1..2), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let expected_results = vec![ + "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "N9", + ]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(10..12), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(10..12), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_for_props_added_at_different_times() { + let filter = NodeFilter + .property("q1") + .eq(0u64) + .and(NodeFilter.property("p1").eq(3u64)); + let expected_results = vec!["N10", "N11", "N12", "N13"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(1..4), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(1..4), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_nodes_filters_pg_for_props_added_at_different_times() { + let filter = NodeFilter + .property("q1") + .eq(0u64) + .and(NodeFilter.property("p1").eq(3u64)); + let expected_results = vec!["N10", "N11", "N12", "N13"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_fuzzy_search() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter + .property("k2") + .fuzzy_search("Paper_Airpla", 2, false); + let expected_results = vec!["N1"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_fuzzy_search() { + let filter = NodeFilter + .property("k2") + .fuzzy_search("Paper_Air", 5, false); + let expected_results = vec!["N1", "N2", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_nodes_filters_fuzzy_search_prefix_match() { + // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 + let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec!["N1", "N2"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + + let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, false); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::Graph], + ); + } + + #[test] + fn test_nodes_filters_pg_fuzzy_search_prefix_match() { + let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec!["N1", "N2", "N7"]; + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, false); + let expected_results = Vec::<&str>::new(); + assert_filter_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + } + + mod test_edges_filters_window_graph { + use raphtory::{ + db::{ + api::view::{filter_ops::Filter, StaticGraphViewOps}, + graph::views::filter::model::{ + edge_filter::EdgeFilter, node_filter::ops::NodeFilterOps, + property_filter::ops::PropertyFilterOps, ComposableFilter, + PropertyFilterFactory, + }, + }, + errors::GraphError, + prelude::{AdditionOps, Graph, GraphViewOps, PropertyAdditionOps, TimeOps, NO_PROPS}, + }; + use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; + use raphtory_test_utils::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, + TestVariants, WindowGraphTransformer, + }; + + fn init_graph(graph: G) -> G { + let edges = vec![ + ( + 6, + "N1", + "N2", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ( + 7, + "N1", + "N2", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(5i64)), + ("k3", Prop::Bool(false)), + ], + Some("air_nomad"), + ), + ( + 6, + "N2", + "N3", + vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(6.0f64))], + Some("water_tribe"), + ), + ( + 7, + "N2", + "N3", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("water_tribe"), + ), + ( + 8, + "N3", + "N4", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + ( + 9, + "N4", + "N5", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + ( + 5, + "N5", + "N6", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ( + 6, + "N5", + "N6", + vec![ + ("p1", Prop::U64(2u64)), + ("k2", Prop::Str(ArcStr::from("Pometry"))), + ("k4", Prop::F64(1.0f64)), + ], + Some("air_nomad"), + ), + ( + 5, + "N6", + "N7", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + ( + 6, + "N6", + "N7", + vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(1.0f64))], + Some("fire_nation"), + ), + ( + 3, + "N7", + "N8", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("air_nomad"), + ), + ( + 5, + "N7", + "N8", + vec![("p1", Prop::U64(1u64))], + Some("air_nomad"), + ), + ( + 3, + "N8", + "N9", + vec![("p1", Prop::U64(1u64))], + Some("fire_nation"), + ), + ( + 4, + "N8", + "N9", + vec![ + ("p1", Prop::U64(2u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + Some("fire_nation"), + ), + (2, "N9", "N10", vec![("p1", Prop::U64(2u64))], None), + (2, "N10", "N11", vec![("q1", Prop::U64(0u64))], None), + (2, "N10", "N11", vec![("p1", Prop::U64(3u64))], None), + (2, "N11", "N12", vec![("p1", Prop::U64(3u64))], None), + (2, "N11", "N12", vec![("q1", Prop::U64(0u64))], None), + (2, "N12", "N13", vec![("q1", Prop::U64(0u64))], None), + ( + 3, + "N12", + "N13", + vec![ + ("p1", Prop::U64(3u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + None, + ), + (2, "N13", "N14", vec![("q1", Prop::U64(0u64))], None), + (3, "N13", "N14", vec![("p1", Prop::U64(3u64))], None), + (2, "N14", "N15", vec![("q1", Prop::U64(0u64))], None), + (2, "N15", "N1", vec![], None), + ]; + + for (id, src, dst, props, layer) in &edges { + graph + .add_edge(*id, src, dst, props.clone(), *layer) + .unwrap(); + } + + // Metadata property assignments + let metadata = vec![ + ( + "N1", + "N2", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(3i64)), + ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(6.0f64)), + ], + Some("air_nomad"), + ), + ("N4", "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), + ("N9", "N10", vec![("p1", Prop::U64(1u64))], None), + ("N10", "N11", vec![("p1", Prop::U64(1u64))], None), + ("N11", "N12", vec![("p1", Prop::U64(1u64))], None), + ("N12", "N13", vec![("p1", Prop::U64(1u64))], None), + ( + "N13", + "N14", + vec![ + ("p1", Prop::U64(1u64)), + ("k1", Prop::I64(2i64)), + ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), + ("k3", Prop::Bool(true)), + ("k4", Prop::F64(10.0f64)), + ], + None, + ), + ("N14", "N15", vec![("p1", Prop::U64(1u64))], None), + ("N15", "N1", vec![("p1", Prop::U64(1u64))], None), + ]; + + for (src, dst, props, layer) in metadata { + graph + .edge(src, dst) + .unwrap() + .add_metadata(props, layer) + .unwrap(); + } + + graph.add_node(1, "N1", NO_PROPS, None, None).unwrap(); + graph.add_node(2, "N2", NO_PROPS, None, None).unwrap(); + graph.add_node(3, "N3", NO_PROPS, None, None).unwrap(); + + graph + } + + fn init_graph2(graph: G) -> G { + let edges = vec![( + 2, + "N14", + "N15", + vec![ + ("q1", Prop::U64(0u64)), + ( + "x", + Prop::list(vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)]), + ), + ], + None, + )]; + + for (id, src, dst, props, layer) in &edges { + graph + .add_edge(*id, src, dst, props.clone(), *layer) + .unwrap(); + } + + graph + } + + #[test] + fn test_edges_filters_for_src_eq() { + let filter = EdgeFilter::src().name().eq("N2"); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_src_eq() { + let filter = EdgeFilter::src().name().eq("N2"); + let expected_results = vec!["N2->N3"]; + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_src_ne() { + let filter = EdgeFilter::src().name().ne("N2"); + let expected_results = vec!["N1->N2", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_src_ne() { + let filter = EdgeFilter::src().name().ne("N2"); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", "N15->N1", + "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", + ]; + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + } + + #[test] + fn test_edges_filters_for_dst_in() { + let filter = EdgeFilter::dst().name().is_in(vec!["N2"]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter::dst().name().is_in(vec!["N2", "N5"]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_dst_in() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter::dst().name().is_in(vec!["N2"]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter::dst().name().is_in(vec!["N2", "N5"]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_dst_not_in() { + let filter = EdgeFilter::dst().name().is_not_in(vec!["N5"]); + let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_dst_not_in() { + let filter = EdgeFilter::dst().name().is_not_in(vec!["N5"]); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", "N15->N1", + "N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", + ]; + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_property_eq() { + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").eq(2i64); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k2").eq("Paper_Airplane"); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k3").eq(true); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").eq(6.0f64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("x").eq(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = vec!["N14->N15"]; + // TODO: List(U64) not supported as disk_graph property + // assert_filter_edges_results_w!( + // init_graph2, + // filter, + // 1..9, + // expected_results, + // variants = [graph] + // ); + assert_filter_edges_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + // TODO: Search APIs don't support list yet + // assert_search_edges_results( + // init_graph2, + // WindowGraphTransformer(6..9), + // filter, + // &expected_results, + // TestVariants::PersistentOnly, + // ); + } + + #[test] + fn test_edges_filters_pg_for_property_eq() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").eq(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").eq(2i64); + + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k2").eq("Paper_Airplane"); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k3").eq(true); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").eq(6.0f64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("x").eq(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = vec!["N14->N15"]; + // TODO: List(U64) not supported as disk_graph property + // assert_filter_edges_results_pg_w!( + // init_graph2, + // filter, + // 1..9, + // expected_results, + // variants = [] + // ); + assert_filter_edges_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + // TODO: Search APIs don't support list yet + // assert_search_edges_results( + // init_graph2, + // WindowGraphTransformer(1..9), + // filter.clone(), + // &expected_results, + // vec![TestGraphVariants::PersistentGraph], + // ); + } + + #[test] + fn test_edges_filters_for_property_ne() { + let filter = EdgeFilter.property("p1").ne(1u64); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").ne(2i64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k2").ne("Paper_Airplane"); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k3").ne(true); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").ne(6.0f64); + let expected_results = vec!["N2->N3", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_property_ne() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").ne(1u64); + let expected_results = vec![ + "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", + "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").ne(2i64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k2").ne("Paper_Airplane"); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k3").ne(true); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").ne(6.0f64); + let expected_results = + vec!["N12->N13", "N2->N3", "N5->N6", "N6->N7", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("x").ne(Prop::list(vec![ + Prop::U64(1), + Prop::U64(6), + Prop::U64(9), + ])); + let expected_results = Vec::<&str>::new(); + assert_filter_edges_results( + init_graph2, + WindowGraphTransformer(1..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + // TODO: Search APIs don't support list yet + // assert_search_edges_results( + // init_graph2, + // WindowGraphTransformer(1..9), + // filter.clone(), + // &expected_results, + // TestVariants::PersistentOnly, + // ); + } + + #[test] + fn test_edges_filters_for_property_lt() { + let filter = EdgeFilter.property("p1").lt(3u64); + let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").lt(3i64); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").lt(10.0f64); + let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_property_lt() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").lt(3u64); + let expected_results = vec![ + "N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").lt(3i64); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").lt(10.0f64); + let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_property_le() { + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").le(2i64); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").le(6.0f64); + let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_property_le() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").le(1u64); + let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").le(2i64); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").le(6.0f64); + let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_property_gt() { + let filter = EdgeFilter.property("p1").gt(1u64); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").gt(2i64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").gt(6.0f64); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("x").gt(Prop::List( + vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)].into(), + )); + let graph = init_graph(Graph::new()); + assert!(matches!( + graph.window(1, 9).filter(filter.clone()).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "x" + )); + assert!(matches!( + graph.persistent_graph().window(1, 9).filter(filter).unwrap_err(), + GraphError::PropertyMissingError(ref name) if name == "x" + )); + } + + #[test] + fn test_edges_filters_pg_for_property_gt() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").gt(1u64); + let expected_results = vec![ + "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", + "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").gt(2i64); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").gt(6.0f64); + let expected_results = vec!["N12->N13", "N2->N3", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_property_ge() { + let filter = EdgeFilter.property("p1").ge(1u64); + let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").ge(2i64); + let expected_results = vec!["N1->N2", "N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").ge(6.0f64); + let expected_results = vec!["N1->N2", "N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_property_ge() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").ge(1u64); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N3->N4", + "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").ge(2i64); + let expected_results = + vec!["N1->N2", "N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").ge(6.0f64); + let expected_results = vec!["N1->N2", "N12->N13", "N2->N3", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_property_in() { + let filter = EdgeFilter.property("p1").is_in(vec![2u64.into()]); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").is_in(vec![2i64.into()]); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter + .property("k2") + .is_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k3").is_in(vec![true.into()]); + let expected_results = vec!["N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").is_in(vec![6.0f64.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_property_in() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").is_in(vec![2u64.into()]); + let expected_results = vec!["N2->N3", "N5->N6", "N8->N9", "N9->N10"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").is_in(vec![2i64.into()]); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter + .property("k2") + .is_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k3").is_in(vec![true.into()]); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").is_in(vec![6.0f64.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_property_not_in() { + let filter = EdgeFilter.property("p1").is_not_in(vec![1u64.into()]); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k1").is_not_in(vec![2i64.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter + .property("k2") + .is_not_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N2->N3", "N5->N6"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k3").is_not_in(vec![true.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k4").is_not_in(vec![6.0f64.into()]); + let expected_results = vec!["N2->N3", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_property_not_in() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").is_not_in(vec![1u64.into()]); + let expected_results = vec![ + "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", + "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k1").is_not_in(vec![2i64.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter + .property("k2") + .is_not_in(vec!["Paper_Airplane".into()]); + let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k3").is_not_in(vec![true.into()]); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k4").is_not_in(vec![6.0f64.into()]); + let expected_results = + vec!["N12->N13", "N2->N3", "N5->N6", "N6->N7", "N7->N8", "N8->N9"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_property_is_some() { + let filter = EdgeFilter.property("p1").is_some(); + let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + fn test_edges_filters_pg_for_property_is_some() { + // TODO: Const properties not supported for disk_graph. + let filter = EdgeFilter.property("p1").is_some(); + let expected_results = vec![ + "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N3->N4", + "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", + ]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_for_src_dst() { + let filter = EdgeFilter::src() + .name() + .eq("N1") + .and(EdgeFilter::dst().name().eq("N2")); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::All, + ); + } + + #[test] + fn test_edges_filters_fuzzy_search() { + let filter = EdgeFilter + .property("k2") + .fuzzy_search("Paper_Airpla", 2, false); + let expected_results = vec!["N1->N2"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + #[ignore] + fn test_edges_filters_pg_fuzzy_search() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("k2").fuzzy_search("Paper_", 2, false); + let expected_results = vec!["N1->N2", "N2->N3", "N7->N8"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + + #[test] + fn test_edges_filters_fuzzy_search_prefix_match() { + let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec!["N1->N2", "N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + + let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec!["N1->N2", "N2->N3"]; + assert_filter_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::EventOnly, + ); + } + + #[test] + #[ignore] + fn test_edges_filters_pg_fuzzy_search_prefix_match() { + // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. + let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); + let expected_results = vec![ + "N1->N2", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N7->N8", "N8->N9", + ]; + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + + let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, false); + let expected_results = Vec::<&str>::new(); + assert_search_edges_results( + init_graph, + WindowGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } + } +} diff --git a/raphtory/Cargo.toml b/raphtory/Cargo.toml index 88c3ac424d..47d877aecd 100644 --- a/raphtory/Cargo.toml +++ b/raphtory/Cargo.toml @@ -118,7 +118,6 @@ streaming-stats = { workspace = true } indoc = { workspace = true } proptest.workspace = true proptest-derive.workspace = true -# raphtory = { workspace = true, features = ["test-utils"] } [target.'cfg(target_os = "macos")'.dependencies] tikv-jemallocator = "0.6.1" @@ -127,7 +126,7 @@ tikv-jemallocator = "0.6.1" prost-build = { workspace = true, optional = true } [features] -default = ["test-utils"] # we can't depend on ourselves but we want to share test-utils +default = [] # we can't depend on ourselves but we want to share test-utils # enables progress bars progress = ["dep:kdam"] @@ -186,8 +185,3 @@ proto = [ "dep:memmap2", "io", ] - -test-utils = [ - "dep:proptest", - "dep:proptest-derive" -] diff --git a/raphtory/examples/eth_loader.rs b/raphtory/examples/eth_loader.rs index b8944fda4f..565454eeb6 100644 --- a/raphtory/examples/eth_loader.rs +++ b/raphtory/examples/eth_loader.rs @@ -1,5 +1,6 @@ #[cfg(feature = "io")] use raphtory::io::parquet_loaders::load_edges_from_parquet; +#[cfg(feature = "io")] use raphtory::{arrow_loader::df_loaders::edges::ColumnNames, errors::GraphError, prelude::*}; use std::path::{Path, PathBuf}; diff --git a/raphtory/src/db/api/storage/graph/storage_ops/property_schema.rs b/raphtory/src/db/api/storage/graph/storage_ops/property_schema.rs index 99e88ecc4e..6ea4ff7f75 100644 --- a/raphtory/src/db/api/storage/graph/storage_ops/property_schema.rs +++ b/raphtory/src/db/api/storage/graph/storage_ops/property_schema.rs @@ -3,7 +3,6 @@ use crate::db::api::{ view::BoxedLIter, }; use raphtory_api::{core::storage::arc_str::ArcStr, iter::IntoDynBoxed}; -use raphtory_storage::core_ops::CoreGraphOps; use super::GraphStorage; diff --git a/raphtory/src/db/graph/mod.rs b/raphtory/src/db/graph/mod.rs index 04b339d461..d354d97f93 100644 --- a/raphtory/src/db/graph/mod.rs +++ b/raphtory/src/db/graph/mod.rs @@ -1,8 +1,6 @@ use raphtory_api::core::storage::dict_mapper::DictMapper; use std::sync::Arc; -#[cfg(any(test, feature = "test-utils"))] -pub mod assertions; pub mod edge; pub mod edges; pub mod graph; diff --git a/raphtory/src/db/graph/views/deletion_graph.rs b/raphtory/src/db/graph/views/deletion_graph.rs index 075a39163f..695b643075 100644 --- a/raphtory/src/db/graph/views/deletion_graph.rs +++ b/raphtory/src/db/graph/views/deletion_graph.rs @@ -30,9 +30,12 @@ use std::{ }; use storage::{ api::graph_props::{GraphPropEntryOps, GraphPropRefOps}, - Config, Extension, + Config, }; +#[cfg(feature = "io")] +use storage::Extension; + /// A graph view where an edge remains active from the time it is added until it is explicitly marked as deleted. /// /// Note that the graph will give you access to all edges that were added at any point in time, even those that are marked as deleted. diff --git a/raphtory/src/db/graph/views/property_redacted_graph.rs b/raphtory/src/db/graph/views/property_redacted_graph.rs index e6eace9e47..c06aa001d7 100644 --- a/raphtory/src/db/graph/views/property_redacted_graph.rs +++ b/raphtory/src/db/graph/views/property_redacted_graph.rs @@ -1,8 +1,7 @@ use crate::db::api::{ properties::internal::{ - EdgePropertySchemaOps, InheritEdgePropertySchemaOps, InheritNodePropertySchemaOps, - InheritTemporalPropertyViewOps, InternalMetadataOps, InternalTemporalPropertiesOps, - NodePropertySchemaOps, + EdgePropertySchemaOps, InheritTemporalPropertyViewOps, InternalMetadataOps, + InternalTemporalPropertiesOps, NodePropertySchemaOps, }, view::{ internal::{ diff --git a/raphtory/src/lib.rs b/raphtory/src/lib.rs index 2cfcd05fd1..980816865f 100644 --- a/raphtory/src/lib.rs +++ b/raphtory/src/lib.rs @@ -114,9 +114,6 @@ pub mod parquet_encoder; pub mod serialise; pub mod storage; -#[cfg(any(test, feature = "test-utils"))] -pub mod test_utils; - /// Return Raphtory crate version. /// /// Returns: diff --git a/raphtory/tests/algo_tests/centrality.rs b/raphtory/tests/algo_tests/centrality.rs deleted file mode 100644 index 2e04370593..0000000000 --- a/raphtory/tests/algo_tests/centrality.rs +++ /dev/null @@ -1,314 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use std::collections::HashMap; - - use crate::algo_tests::assert_eq_hashmaps_approx; - use itertools::Itertools; - use raphtory::{ - algorithms::centrality::{ - betweenness::betweenness_centrality, degree_centrality::degree_centrality, hits::hits, - pagerank::unweighted_page_rank, - }, - prelude::*, - test_storage, - }; - - #[test] - fn test_betweenness_centrality() { - let graph = Graph::new(); - let vs = vec![ - (1, 2), - (1, 3), - (1, 4), - (2, 3), - (2, 4), - (2, 5), - (3, 4), - (3, 5), - (3, 6), - (4, 3), - (4, 2), - (4, 4), - ]; - for (src, dst) in &vs { - graph.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let mut expected: HashMap = HashMap::new(); - expected.insert("1".to_string(), 0.0); - expected.insert("2".to_string(), 1.0); - expected.insert("3".to_string(), 4.0); - expected.insert("4".to_string(), 1.0); - expected.insert("5".to_string(), 0.0); - expected.insert("6".to_string(), 0.0); - - let res = betweenness_centrality(graph, None, false) - .to_hashmap(|value| value.betweenness_centrality); - assert_eq!(res, expected); - - let mut expected: HashMap = HashMap::new(); - expected.insert("1".to_string(), 0.0); - expected.insert("2".to_string(), 0.05); - expected.insert("3".to_string(), 0.2); - expected.insert("4".to_string(), 0.05); - expected.insert("5".to_string(), 0.0); - expected.insert("6".to_string(), 0.0); - let res = betweenness_centrality(graph, None, true) - .to_hashmap(|value| value.betweenness_centrality); - assert_eq!(res, expected); - }); - } - - #[test] - fn test_degree_centrality() { - let graph = Graph::new(); - let vs = vec![(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]; - for (src, dst) in &vs { - graph.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let mut expected: HashMap = HashMap::new(); - expected.insert("1".to_string(), 1.0); - expected.insert("2".to_string(), 1.0); - expected.insert("3".to_string(), 2.0 / 3.0); - expected.insert("4".to_string(), 2.0 / 3.0); - - let res = degree_centrality(graph).to_hashmap(|value| value.score); - assert_eq!(res, expected); - }); - } - - #[test] - fn test_hits() { - let edges = vec![ - (1, 4), - (2, 3), - (2, 5), - (3, 1), - (4, 2), - (4, 3), - (5, 2), - (5, 3), - (5, 4), - (5, 6), - (6, 3), - (6, 8), - (7, 1), - (7, 3), - (8, 1), - ]; - let graph = Graph::new(); - - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let results = hits(graph, 20, None); - - let expected = HashMap::from([ - ("1".to_string(), (0.0431365, 0.096625775)), - ("2".to_string(), (0.14359662, 0.18366566)), - ("3".to_string(), (0.030866561, 0.36886504)), - ("4".to_string(), (0.1865414, 0.12442485)), - ("5".to_string(), (0.26667944, 0.05943252)), - ("6".to_string(), (0.14359662, 0.10755368)), - ("7".to_string(), (0.15471625, 0.0)), - ("8".to_string(), (0.030866561, 0.05943252)), - ]); - - assert_eq!(results.len(), 8); - for (node, value) in results.iter() { - let expected_value = expected.get(&node.name()).unwrap(); - assert!( - (value.hub_score - expected_value.0).abs() < 1e-6, - "mismatched hub score for node {}, expected {}, actual {}", - node.name(), - expected_value.0, - value.hub_score - ); - assert!( - (value.auth_score - expected_value.1).abs() < 1e-6, - "mismatched authority score for node {}, expected {}, actual {}", - node.name(), - expected_value.1, - value.auth_score - ); - } - }) - } - - #[test] - fn test_page_rank() { - let graph = Graph::new(); - - let edges = vec![(1, 2), (1, 4), (2, 3), (3, 1), (4, 1)]; - - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let expected: HashMap = HashMap::from([ - ("1".to_string(), 0.38694), - ("2".to_string(), 0.20195), - ("3".to_string(), 0.20916), - ("4".to_string(), 0.20195), - ]); - let results = unweighted_page_rank(graph, Some(1000), Some(1), None, true, None) - .to_hashmap(|value| value.score); - assert_eq_hashmaps_approx(&results, &expected, 1e-5); - }); - } - - #[test] - fn motif_page_rank() { - let edges = vec![ - (1, 2, 1), - (1, 3, 2), - (1, 4, 3), - (3, 1, 4), - (3, 4, 5), - (3, 5, 6), - (4, 5, 7), - (5, 6, 8), - (5, 8, 9), - (7, 5, 10), - (8, 5, 11), - (1, 9, 12), - (9, 1, 13), - (6, 3, 14), - (4, 8, 15), - (8, 3, 16), - (5, 10, 17), - (10, 5, 18), - (10, 8, 19), - (1, 11, 20), - (11, 1, 21), - (9, 11, 22), - (11, 9, 23), - ]; - - let graph = Graph::new(); - - for (src, dst, t) in edges { - graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let expected: HashMap = HashMap::from([ - ("10".to_string(), 0.072082), - ("8".to_string(), 0.136473), - ("3".to_string(), 0.15484), - ("6".to_string(), 0.07208), - ("11".to_string(), 0.06186), - ("2".to_string(), 0.03557), - ("1".to_string(), 0.11284), - ("4".to_string(), 0.07944), - ("7".to_string(), 0.01638), - ("9".to_string(), 0.06186), - ("5".to_string(), 0.19658), - ]); - - let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None) - .to_hashmap(|value| value.score); - - assert_eq_hashmaps_approx(&results, &expected, 1e-5); - }); - } - - #[test] - fn two_nodes_page_rank() { - let edges = vec![(1, 2), (2, 1)]; - - let graph = Graph::new(); - - for (t, (src, dst)) in edges.into_iter().enumerate() { - graph.add_edge(t as i64, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let expected: HashMap = - HashMap::from([("1".to_string(), 0.5), ("2".to_string(), 0.5)]); - - let results = unweighted_page_rank(graph, Some(1000), Some(4), None, false, None) - .to_hashmap(|value| value.score); - - assert_eq_hashmaps_approx(&results, &expected, 1e-3); - }); - } - - #[test] - fn three_nodes_page_rank_one_dangling() { - let edges = vec![(1, 2), (2, 1), (2, 3)]; - - let graph = Graph::new(); - - for (t, (src, dst)) in edges.into_iter().enumerate() { - graph.add_edge(t as i64, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let expected: HashMap = HashMap::from([ - ("1".to_string(), 0.303), - ("2".to_string(), 0.393), - ("3".to_string(), 0.303), - ]); - - let results = unweighted_page_rank(graph, Some(10), Some(4), None, false, None) - .to_hashmap(|value| value.score); - - assert_eq_hashmaps_approx(&results, &expected, 1e-3); - }); - } - - #[test] - fn dangling_page_rank() { - let edges = vec![ - (1, 2), - (1, 3), - (2, 3), - (3, 1), - (3, 2), - (3, 4), - // dangling from here - (4, 5), - (5, 6), - (6, 7), - (7, 8), - (8, 9), - (9, 10), - (10, 11), - ] - .into_iter() - .enumerate() - .map(|(t, (src, dst))| (src, dst, t as i64)) - .collect_vec(); - - let graph = Graph::new(); - - for (src, dst, t) in edges { - graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let expected: HashMap = HashMap::from([ - ("1".to_string(), 0.055), - ("2".to_string(), 0.079), - ("3".to_string(), 0.113), - ("4".to_string(), 0.055), - ("5".to_string(), 0.070), - ("6".to_string(), 0.083), - ("7".to_string(), 0.093), - ("8".to_string(), 0.102), - ("9".to_string(), 0.110), - ("10".to_string(), 0.117), - ("11".to_string(), 0.122), - ]); - - let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None) - .to_hashmap(|value| value.score); - - assert_eq_hashmaps_approx(&results, &expected, 1e-3); - }); - } -} diff --git a/raphtory/tests/algo_tests/community_detection.rs b/raphtory/tests/algo_tests/community_detection.rs deleted file mode 100644 index 8167f878e0..0000000000 --- a/raphtory/tests/algo_tests/community_detection.rs +++ /dev/null @@ -1,220 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use raphtory::{ - algorithms::community_detection::{ - label_propagation::label_propagation, - louvain::louvain, - modularity::{ComID, ModularityFunction, ModularityUnDir, Partition}, - }, - logging::global_info_logger, - prelude::*, - test_storage, - }; - use raphtory_core::entities::VID; - use std::collections::{HashMap, HashSet}; - use tracing::info; - - fn group_by_value(map: &HashMap) -> Vec> { - let mut grouped: HashMap> = HashMap::new(); - - for (key, &value) in map { - grouped - .entry(value) - .or_insert_with(HashSet::new) - .insert(key.clone()); - } - - grouped.into_values().collect() - } - - #[test] - fn lpa_test() { - let graph: Graph = Graph::new(); - let edges = vec![ - (1, "R1", "R2"), - (1, "R1", "R3"), - (1, "R2", "R3"), - (1, "R3", "G"), - (1, "G", "B1"), - (1, "G", "B3"), - (1, "B1", "B2"), - (1, "B2", "B3"), - (1, "B2", "B4"), - (1, "B3", "B4"), - (1, "B3", "B5"), - (1, "B4", "B5"), - ]; - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let seed = Some([5; 32]); - let result = - label_propagation(graph, 20, seed, None).to_hashmap(|value| value.community_id); - println!("{:?}", result); - let result = group_by_value(&result); - - let expected = vec![ - HashSet::from(["R1".to_string(), "R2".to_string(), "R3".to_string()]), - HashSet::from([ - "G".to_string(), - "B1".to_string(), - "B2".to_string(), - "B3".to_string(), - "B4".to_string(), - "B5".to_string(), - ]), - ]; - for hashset in expected { - assert!(result.contains(&hashset)); - } - }); - } - - use proptest::prelude::*; - - #[test] - fn test_louvain() { - let edges = vec![ - (100, 200, 2.0f64), - (100, 300, 3.0f64), - (200, 300, 8.5f64), - (300, 400, 1.0f64), - (400, 500, 1.5f64), - (600, 800, 0.5f64), - (700, 900, 3.5f64), - (100, 600, 1.5f64), - ]; - test_all_nodes_assigned_inner(edges) - } - - fn test_all_nodes_assigned_inner(edges: Vec<(u64, u64, f64)>) { - let graph = Graph::new(); - for (src, dst, weight) in edges { - graph - .add_edge(1, src, dst, [("weight", weight)], None) - .unwrap(); - graph - .add_edge(1, dst, src, [("weight", weight)], None) - .unwrap(); - } - - test_storage!(&graph, |graph| { - let result = louvain::(graph, 1.0, Some("weight"), None); - assert!(graph - .nodes() - .iter() - .all(|n| result.get_by_node(n).is_some())); - }); - } - - fn test_all_nodes_assigned_inner_unweighted(edges: Vec<(u64, u64)>) { - let graph = Graph::new(); - for (src, dst) in edges { - graph.add_edge(1, src, dst, NO_PROPS, None).unwrap(); - graph.add_edge(1, dst, src, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let result = louvain::(graph, 1.0, None, None); - assert!(graph - .nodes() - .iter() - .all(|n| result.get_by_node(n).is_some())); - }); - } - - proptest! { - #[test] - fn test_all_nodes_in_communities(edges in any::>().prop_map(|mut v| {v.iter_mut().for_each(|(_, _, w)| *w = w.abs()); v})) { - test_all_nodes_assigned_inner(edges) - } - - #[test] - fn test_all_nodes_assigned_unweighted(edges in any::>().prop_map(|v| v.into_iter().map(|(s, d)| (s as u64, d as u64)).collect::>())) { - test_all_nodes_assigned_inner_unweighted(edges) - } - } - - #[cfg(feature = "io")] - #[test] - fn lfr_test() { - use raphtory::io::csv_loader::CsvLoader; - use raphtory_api::core::utils::logging::global_info_logger; - use serde::{Deserialize, Serialize}; - use std::path::PathBuf; - global_info_logger(); - let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - d.push("resources/test"); - let loader = CsvLoader::new(d.join("test.csv")).set_delimiter(","); - let graph = Graph::new(); - - #[derive(Deserialize, Serialize, Debug)] - struct CsvEdge { - src: u64, - dst: u64, - } - - loader - .load_into_graph(&graph, |e: CsvEdge, g| { - g.add_edge(1, e.src, e.dst, NO_PROPS, None).unwrap(); - }) - .unwrap(); - - test_storage!(&graph, |graph| { - let _ = louvain::(graph, 1.0, None, None); - // TODO: Add assertions - }); - } - - #[test] - fn test_delta() { - global_info_logger(); - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let mut m = ModularityUnDir::new( - graph, - None, - 1.0, - Partition::new_singletons(graph.count_nodes()), - 1e-8, - ); - let old_value = m.value(); - assert_eq!(old_value, -0.5); - let delta = m.move_delta(&VID(0), ComID(1)); - info!("delta: {delta}"); - m.move_node(&VID(0), ComID(1)); - assert_eq!(m.value(), old_value + delta) - }); - } - - #[test] - fn test_aggregation() { - global_info_logger(); - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 0, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); - graph.add_edge(0, 0, 3, NO_PROPS, None).unwrap(); - graph.add_edge(0, 3, 0, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let partition = Partition::from_iter([0usize, 0, 1, 1]); - let mut m = ModularityUnDir::new(graph, None, 1.0, partition, 1e-8); - let value_before = m.value(); - let _ = m.aggregate(); - let value_after = m.value(); - info!("before: {value_before}, after: {value_after}"); - assert_eq!(value_after, value_before); - let delta = m.move_delta(&VID(0), ComID(1)); - m.move_node(&VID(0), ComID(1)); - let value_merged = m.value(); - assert_eq!(value_merged, 0.0); - assert!((value_merged - (value_after + delta)).abs() < 1e-8); - }); - } -} diff --git a/raphtory/tests/algo_tests/components.rs b/raphtory/tests/algo_tests/components.rs deleted file mode 100644 index ca449e5a18..0000000000 --- a/raphtory/tests/algo_tests/components.rs +++ /dev/null @@ -1,954 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use ahash::HashSet; - use proptest::{prelude::Strategy, proptest, sample::Index}; - use raphtory::{ - algorithms::components::{weakly_connected_components, ConnectedComponent}, - db::api::{ - mutation::AdditionOps, - state::{NodeStateValue, TypedNodeState}, - view::internal::GraphView, - }, - prelude::*, - test_storage, - }; - use serde::{Deserialize, Serialize}; - use std::{ - collections::{BTreeSet, HashMap}, - fmt::{Debug, Formatter}, - hash::Hash, - }; - - fn assert_same_partition< - V: NodeStateValue + Hash + Eq + 'static, - G: GraphView + 'static, - ID: Into, - >( - left: TypedNodeState<'static, V, G>, // NodeState, - right: impl IntoIterator>, - ) { - let left_groups: HashSet> = left - .groups() - .into_iter_groups() - .map(|(_, nodes)| nodes.id().collect()) - .collect(); - let right_groups: HashSet> = right - .into_iter() - .map(|inner| inner.into_iter().map(|id| id.into()).collect()) - .collect(); - assert_eq!(left_groups, right_groups); - } - - #[test] - fn run_loop_simple_connected_components() { - let graph = Graph::new(); - - let edges = vec![ - (1, 2, 1), - (2, 3, 2), - (3, 4, 3), - (3, 5, 4), - (6, 5, 5), - (7, 8, 6), - (8, 7, 7), - ]; - - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - for _ in 0..1000 { - let results = weakly_connected_components(graph); - assert_same_partition(results, [1..=6, 7..=8]); - } - }); - } - - #[test] - fn simple_connected_components_2() { - let graph = Graph::new(); - - let edges = vec![ - (1, 2, 1), - (1, 3, 2), - (1, 4, 3), - (3, 1, 4), - (3, 4, 5), - (3, 5, 6), - (4, 5, 7), - (5, 6, 8), - (5, 8, 9), - (7, 5, 10), - (8, 5, 11), - (1, 9, 12), - (9, 1, 13), - (6, 3, 14), - (4, 8, 15), - (8, 3, 16), - (5, 10, 17), - (10, 5, 18), - (10, 8, 19), - (1, 11, 20), - (11, 1, 21), - (9, 11, 22), - (11, 9, 23), - ]; - - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let results = weakly_connected_components(graph); - assert_same_partition(results, [1..=11]); - }); - } - - #[test] - fn test_multiple_components() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (2, 2, 1), - (3, 3, 1), - (1, 10, 11), - (2, 20, 21), - (3, 30, 31), - ]; - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - for _ in 0..1000 { - let result = weakly_connected_components(&graph); - assert_same_partition( - result, - [vec![1, 2, 3], vec![10, 11], vec![20, 21], vec![30, 31]], - ) - } - } - - // connected community_detection on a graph with 1 node and a self loop - #[test] - fn simple_connected_components_3() { - let graph = Graph::new(); - - let edges = vec![(1, 1, 1)]; - - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - for _ in 0..1000 { - // loop to test for weird non-deterministic behaviour - let results = weakly_connected_components(graph); - assert_same_partition(results, [[1]]); - } - }); - } - - #[test] - fn windowed_connected_components() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).expect("add edge"); - graph.add_edge(0, 2, 1, NO_PROPS, None).expect("add edge"); - graph.add_edge(9, 3, 4, NO_PROPS, None).expect("add edge"); - graph.add_edge(9, 4, 3, NO_PROPS, None).expect("add edge"); - - test_storage!(&graph, |graph| { - let results = weakly_connected_components(graph); - assert_same_partition(results, [[1, 2], [3, 4]]); - - let wg = graph.window(0, 2); - let results = weakly_connected_components(&wg); - assert_same_partition(results, [[1, 2]]); - }); - } - - #[test] - fn layered_connected_components() { - let g = Graph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("ZERO-TWO")).unwrap(); - g.add_edge(1, 1, 3, NO_PROPS, Some("ZERO-TWO")).unwrap(); - g.add_edge(2, 4, 5, NO_PROPS, Some("ZERO-TWO")).unwrap(); - g.add_edge(3, 6, 7, NO_PROPS, Some("THREE-FIVE")).unwrap(); - g.add_edge(4, 8, 9, NO_PROPS, Some("THREE-FIVE")).unwrap(); - - let g_layer_zero_two = g.layers("ZERO-TWO").unwrap(); - - assert_eq!(g_layer_zero_two.nodes().id(), [1, 2, 3, 4, 5]); - let g_layer_three_five = g.layers("THREE-FIVE").unwrap(); - - let res_zero_two = weakly_connected_components(&g_layer_zero_two); - let c1 = res_zero_two.get_by_node(1).unwrap(); - let c2 = res_zero_two.get_by_node(4).unwrap(); - - let expected_zero_two: HashMap = - [(1, c1), (2, c1), (3, c1), (4, c2), (5, c2)].into(); - - assert_eq!(res_zero_two, expected_zero_two); - - let res_three_five = weakly_connected_components(&g_layer_three_five); - - let c6 = res_three_five.get_by_node(6).unwrap().component_id; - let c7 = res_three_five.get_by_node(8).unwrap().component_id; - - let expected_three_five: HashMap = - [(6u64, c6), (7, c6), (8, c7), (9, c7)].into(); - assert_eq!(res_three_five, expected_three_five); - } - - #[derive(Serialize, Deserialize)] - struct ComponentTestInput { - edges: Vec<(u64, u64)>, - components: Vec>, - } - - impl Debug for ComponentTestInput { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&serde_json::to_string(self).unwrap()) - } - } - - fn random_component_edges( - num_components: usize, - num_nodes_per_component: usize, - ) -> impl Strategy { - let vs = proptest::collection::vec( - proptest::collection::vec( - ( - 0..num_nodes_per_component, - proptest::arbitrary::any::(), - ), - 2..=num_nodes_per_component, - ), - 0..=num_components, - ); - vs.prop_map(move |vs| { - let mut edges = Vec::new(); - let mut components = Vec::new(); - for (ci, c) in vs.into_iter().enumerate() { - let offset = num_nodes_per_component * ci; - let component: Vec<_> = c.iter().map(|(i, _)| (*i + offset) as u64).collect(); - for i in 1..c.len() { - let n = component[c[i].1.index(i)]; - edges.push((component[i], n)); - } - components.push(component.into_iter().collect()); - } - ComponentTestInput { edges, components } - }) - } - - #[test] - fn weakly_connected_components_proptest() { - proptest!(|(input in random_component_edges(10, 100))|{ - let ComponentTestInput {edges, components } = input; - let g = Graph::new(); - for (src, dst) in edges { - g.add_edge(0, src, dst, NO_PROPS, None).unwrap(); - } - for _ in 0..10 { - let result = weakly_connected_components(&g); - assert_same_partition(result, &components); - } - }) - } - - mod in_component_test { - use itertools::Itertools; - use raphtory::{ - algorithms::components::{ - in_component, in_component_filtered, in_components, in_components_filtered, - }, - db::{ - api::mutation::AdditionOps, - graph::views::filter::{ - model::{ - graph_filter::GraphFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, TryAsCompositeFilter, ViewWrapOps, - }, - CreateFilter, - }, - }, - prelude::*, - test_storage, - }; - use std::collections::HashMap; - - fn check_node(graph: &Graph, node_id: u64, mut correct: Vec<(u64, usize)>) { - let mut results: Vec<_> = in_component(graph.node(node_id).unwrap()) - .iter() - .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) - .collect(); - results.sort(); - correct.sort(); - assert_eq!(results, correct); - } - - fn check_node_filtered( - graph: &Graph, - node_id: u64, - filter: F, - mut correct: Vec<(u64, usize)>, - ) { - let mut results: Vec<_> = in_component_filtered(graph.node(node_id).unwrap(), filter) - .unwrap() - .iter() - .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) - .collect(); - - results.sort(); - correct.sort(); - assert_eq!(results, correct); - } - - #[test] - fn in_component_test() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (1, 1, 3), - (1, 2, 4), - (1, 2, 5), - (1, 5, 4), - (1, 4, 6), - (1, 4, 7), - (1, 5, 8), - ]; - - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - check_node(&graph, 1, vec![]); - check_node(&graph, 2, vec![(1, 1)]); - check_node(&graph, 3, vec![(1, 1)]); - check_node(&graph, 4, vec![(1, 2), (2, 1), (5, 1)]); - check_node(&graph, 5, vec![(1, 2), (2, 1)]); - check_node(&graph, 6, vec![(1, 3), (2, 2), (4, 1), (5, 2)]); - check_node(&graph, 7, vec![(1, 3), (2, 2), (4, 1), (5, 2)]); - check_node(&graph, 8, vec![(1, 3), (2, 2), (5, 1)]); - } - - #[test] - fn test_distances() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); - graph.add_edge(0, 5, 3, NO_PROPS, None).unwrap(); - - check_node(&graph, 3, vec![(1, 2), (2, 1), (4, 2), (5, 1)]); - } - - #[test] - fn in_components_test() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (1, 1, 3), - (1, 2, 4), - (1, 2, 5), - (1, 5, 4), - (1, 4, 6), - (1, 4, 7), - (1, 5, 8), - ]; - - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let results = in_components(graph, None); - let mut correct = HashMap::new(); - correct.insert("1".to_string(), vec![]); - correct.insert("2".to_string(), vec![1]); - correct.insert("3".to_string(), vec![1]); - correct.insert("4".to_string(), vec![1, 2, 5]); - correct.insert("5".to_string(), vec![1, 2]); - correct.insert("6".to_string(), vec![1, 2, 4, 5]); - correct.insert("7".to_string(), vec![1, 2, 4, 5]); - correct.insert("8".to_string(), vec![1, 2, 5]); - let map: HashMap> = results - .into_iter() - .map(|(k, v)| { - ( - k.name(), - v.in_components - .into_iter() - .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) - .sorted() - .collect(), - ) - }) - .collect(); - assert_eq!(map, correct); - }); - } - - #[test] - fn in_component_filtered_by_layer_removes_cross_layer_ancestors() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); - - graph.add_edge(1, 2, 5, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 5, 4, NO_PROPS, Some("B")).unwrap(); - - // Sanity: with A-only filter, 5 should disappear - let filter = GraphFilter.layer("A"); - - check_node_filtered(&graph, 6, filter.clone(), vec![(4, 1), (2, 2), (1, 3)]); - check_node_filtered(&graph, 4, filter.clone(), vec![(2, 1), (1, 2)]); - check_node_filtered(&graph, 5, filter.clone(), vec![]); // B-only inbound edges, so none under A - } - - #[test] - fn in_components_filtered_by_layer_matches_expected_node_sets() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); - - graph.add_edge(1, 2, 5, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 5, 4, NO_PROPS, Some("B")).unwrap(); - - test_storage!(&graph, |graph| { - let results = in_components_filtered(graph, None, GraphFilter.layer("A")).unwrap(); - - let mut correct: HashMap> = HashMap::new(); - correct.insert("1".to_string(), vec![]); - correct.insert("2".to_string(), vec![1]); - correct.insert("4".to_string(), vec![1, 2]); - correct.insert("5".to_string(), vec![]); // only reachable via B edges, which are filtered out - correct.insert("6".to_string(), vec![1, 2, 4]); - - let map: HashMap> = results - .into_iter() - .map(|(k, v)| { - ( - k.name(), - v.in_components - .into_iter() - .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) - .sorted() - .collect(), - ) - }) - .collect(); - - assert_eq!(map, correct); - }); - } - - #[test] - fn in_component_filtered_by_layer_handles_multiple_inbound_paths_with_distances() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 4, 6, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); - - // Layer B adds an alternate chain to 4 that should be ignored under A filter - graph.add_edge(1, 10, 11, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 11, 4, NO_PROPS, Some("B")).unwrap(); - - check_node_filtered( - &graph, - 6, - GraphFilter.layer("A"), - vec![(4, 1), (2, 2), (3, 2), (1, 3)], - ); - } - - #[test] - fn in_component_filtered_returns_nodes_that_are_unfiltered_for_future_traversals() { - let graph = Graph::new(); - - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 99, 2, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 100, 99, NO_PROPS, Some("B")).unwrap(); - - let result = - in_component_filtered(graph.node(4).unwrap(), GraphFilter.layer("A")).unwrap(); - let result = result - .iter() - .flat_map(|(n, _)| n.id().as_u64()) - .collect::>(); - assert_eq!(result, vec![2]); - let unfiltered_ids: Vec = - in_component_filtered(graph.node(4).unwrap(), GraphFilter.layer("A")) - .unwrap() - .nodes() - .in_neighbours() - .iter() - .flat_map(|(_, ns)| ns.iter().filter_map(|c| c.id().as_u64())) - .collect(); - - assert_eq!(unfiltered_ids, vec![99]); - } - - #[test] - fn in_component_edge_filtered_handles_multiple_inbound_paths_with_distances() { - let graph = Graph::new(); - - graph - .add_edge(1, 1, 2, vec![("p1", Prop::U64(1))], Some("A")) - .unwrap(); - graph - .add_edge(1, 2, 4, vec![("p1", Prop::U64(2))], Some("A")) - .unwrap(); - graph - .add_edge(1, 4, 6, vec![("p1", Prop::U64(3))], Some("A")) - .unwrap(); - graph - .add_edge(1, 3, 4, vec![("p1", Prop::U64(4))], Some("A")) - .unwrap(); - - // Layer B adds an alternate chain to 4 that should be ignored under A filter - graph - .add_edge(1, 10, 11, vec![("p1", Prop::U64(2))], Some("B")) - .unwrap(); - graph - .add_edge(1, 11, 4, vec![("p1", Prop::U64(2))], Some("B")) - .unwrap(); - - let filter = EdgeFilter.layer("A").property("p1").ge(3u64); - check_node_filtered(&graph, 6, filter, vec![(3, 2), (4, 1)]); - } - } - - #[cfg(test)] - mod components_test { - use itertools::Itertools; - use raphtory::{ - algorithms::components::{ - out_component, out_component_filtered, out_components, out_components_filtered, - }, - db::{ - api::mutation::AdditionOps, - graph::views::filter::{ - model::{ - graph_filter::GraphFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, ViewWrapOps, - }, - CreateFilter, - }, - }, - prelude::*, - test_storage, - }; - use std::collections::HashMap; - - fn check_node(graph: &Graph, node_id: u64, mut correct: Vec<(u64, usize)>) { - let mut results: Vec<_> = out_component(graph.node(node_id).unwrap()) - .iter() - .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) - .collect(); - results.sort(); - correct.sort(); - assert_eq!(results, correct); - } - - fn check_node_filtered( - graph: &Graph, - node_id: u64, - filter: F, - mut correct: Vec<(u64, usize)>, - ) { - let mut results: Vec<_> = out_component_filtered(graph.node(node_id).unwrap(), filter) - .unwrap() - .iter() - .map(|(n, d)| (n.id().as_u64().unwrap(), d.distance)) - .collect(); - results.sort(); - correct.sort(); - assert_eq!(results, correct); - } - - #[test] - fn out_component_test() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (1, 1, 3), - (1, 2, 3), - (1, 2, 4), - (1, 2, 5), - (1, 5, 4), - (1, 4, 6), - (1, 4, 7), - (1, 5, 8), - ]; - - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - check_node( - &graph, - 1, - vec![(2, 1), (3, 1), (4, 2), (5, 2), (6, 3), (7, 3), (8, 3)], - ); - check_node( - &graph, - 2, - vec![(3, 1), (4, 1), (5, 1), (6, 2), (7, 2), (8, 2)], - ); - check_node(&graph, 3, vec![]); - check_node(&graph, 4, vec![(6, 1), (7, 1)]); - check_node(&graph, 5, vec![(4, 1), (6, 2), (7, 2), (8, 1)]); - check_node(&graph, 6, vec![]); - check_node(&graph, 7, vec![]); - check_node(&graph, 8, vec![]); - } - - #[test] - fn test_distances() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); - graph.add_edge(0, 5, 3, NO_PROPS, None).unwrap(); - - check_node(&graph, 1, vec![(2, 1), (3, 2), (4, 1), (5, 2)]); - } - - #[test] - fn out_components_test() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (1, 1, 3), - (1, 2, 4), - (1, 2, 5), - (1, 5, 4), - (1, 4, 6), - (1, 4, 7), - (1, 5, 8), - ]; - - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let results = out_components(graph, None); - - let mut correct = HashMap::new(); - correct.insert("1".to_string(), vec![2, 3, 4, 5, 6, 7, 8]); - correct.insert("2".to_string(), vec![4, 5, 6, 7, 8]); - correct.insert("3".to_string(), vec![]); - correct.insert("4".to_string(), vec![6, 7]); - correct.insert("5".to_string(), vec![4, 6, 7, 8]); - correct.insert("6".to_string(), vec![]); - correct.insert("7".to_string(), vec![]); - correct.insert("8".to_string(), vec![]); - let map: HashMap> = results - .iter() - .map(|(k, v)| { - ( - k.name(), - v.out_components - .into_iter() - .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) - .sorted() - .collect(), - ) - }) - .collect(); - assert_eq!(map, correct); - }); - } - - #[test] - fn out_component_filtered_by_layer_prunes_cross_layer_paths() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 3, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); - - graph.add_edge(1, 2, 10, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 10, 11, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 11, 4, NO_PROPS, Some("B")).unwrap(); - - let filter = GraphFilter.layer("A"); - - check_node_filtered(&graph, 1, filter.clone(), vec![(2, 1), (3, 2), (4, 3)]); - - check_node_filtered(&graph, 2, filter.clone(), vec![(3, 1), (4, 2)]); - - check_node_filtered(&graph, 10, filter.clone(), vec![]); - check_node_filtered(&graph, 11, filter.clone(), vec![]); - } - - #[test] - fn out_component_filtered_by_layer_distances_follow_filtered_graph_only() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 1, 3, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); - - graph.add_edge(1, 1, 99, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); - - let filter = GraphFilter.layer("A"); - - check_node_filtered(&graph, 1, filter, vec![(2, 1), (3, 1), (4, 2)]); - } - - #[test] - fn out_components_filtered_by_layer_matches_expected_node_sets() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 3, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 5, NO_PROPS, Some("A")).unwrap(); - - graph.add_edge(1, 5, 100, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 1, 200, NO_PROPS, Some("B")).unwrap(); - - test_storage!(&graph, |graph| { - let results = out_components_filtered(graph, None, GraphFilter.layer("A")).unwrap(); - - let mut correct: HashMap> = HashMap::new(); - correct.insert("1".to_string(), vec![2, 3, 4, 5]); - correct.insert("2".to_string(), vec![3, 4, 5]); - correct.insert("3".to_string(), vec![4]); - correct.insert("4".to_string(), vec![]); - correct.insert("5".to_string(), vec![]); - correct.insert("100".to_string(), vec![]); - correct.insert("200".to_string(), vec![]); - - let map: HashMap> = results - .into_iter() - .map(|(k, v)| { - ( - k.name(), - v.out_components - .into_iter() - .map(|value| graph.node(value).unwrap().id().as_u64().unwrap()) - .sorted() - .collect(), - ) - }) - .collect(); - - assert_eq!(map, correct); - }); - } - - #[test] - fn out_component_filtered_returns_nodes_that_are_unfiltered_for_future_traversals() { - let graph = Graph::new(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 99, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); - - let unfiltered_ids: Vec = - out_component_filtered(graph.node(1).unwrap(), GraphFilter.layer("A")) - .unwrap() - .nodes() - .out_neighbours() - .iter() - .flat_map(|(_, ns)| ns.iter().filter_map(|c| c.id().as_u64())) - .collect(); - - assert_eq!(unfiltered_ids, vec![99]); - } - - #[test] - fn out_component_node_filtered_distances_follow_filtered_graph_only() { - let graph = Graph::new(); - - graph - .add_node(1, 1, vec![("p1", Prop::U64(1))], None, None) - .unwrap(); - graph - .add_node(1, 2, vec![("p1", Prop::U64(2))], None, None) - .unwrap(); - graph - .add_node(1, 3, vec![("p1", Prop::U64(3))], None, None) - .unwrap(); - graph - .add_node(1, 4, vec![("p1", Prop::U64(4))], None, None) - .unwrap(); - - graph.add_edge(1, 1, 2, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 2, 4, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 1, 3, NO_PROPS, Some("A")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("A")).unwrap(); - - graph.add_edge(1, 1, 99, NO_PROPS, Some("B")).unwrap(); - graph.add_edge(1, 99, 100, NO_PROPS, Some("B")).unwrap(); - - let filter = NodeFilter.property("p1").ge(3u64); - - check_node_filtered(&graph, 1, filter, vec![(3, 1), (4, 2)]); - } - } - - #[cfg(test)] - mod strongly_connected_components_tests { - use itertools::Itertools; - use raphtory::{ - algorithms::components::strongly_connected_components, - prelude::{AdditionOps, Graph, NodeStateGroupBy, NodeStateOps, NodeViewOps, NO_PROPS}, - test_storage, - }; - use std::collections::HashSet; - - #[test] - fn scc_test() { - let graph = Graph::new(); - let edges = vec![ - (1, 1, 2), - (1, 2, 3), - (1, 2, 5), - (1, 3, 4), - (1, 5, 6), - (1, 6, 4), - (1, 6, 7), - (1, 7, 8), - (1, 8, 6), - (1, 6, 2), - ]; - - for (ts, src, dst) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let scc_nodes: HashSet> = strongly_connected_components(graph) - .groups() - .into_iter_groups() - .map(|(_, v)| v.name().into_iter_values().sorted().collect()) - .collect(); - - let expected: HashSet> = [ - vec!["2", "5", "6", "7", "8"], - vec!["1"], - vec!["3"], - vec!["4"], - ] - .into_iter() - .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) - .collect(); - assert_eq!(scc_nodes, expected); - }); - } - - #[test] - fn scc_test_multiple_components() { - let graph = Graph::new(); - let edges = [ - (1, 2), - (2, 3), - (2, 8), - (3, 4), - (3, 7), - (4, 5), - (5, 3), - (5, 6), - (7, 4), - (7, 6), - (8, 1), - (8, 7), - ]; - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let scc_nodes: HashSet> = strongly_connected_components(graph) - .groups() - .into_iter_groups() - .map(|(_, v)| v.name().into_iter_values().sorted().collect()) - .collect(); - - let expected: HashSet> = - [vec!["3", "4", "5", "7"], vec!["1", "2", "8"], vec!["6"]] - .into_iter() - .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) - .collect(); - assert_eq!(scc_nodes, expected); - }); - } - - #[test] - fn scc_test_multiple_components_2() { - let graph = Graph::new(); - let edges = [(1, 2), (1, 3), (1, 4), (4, 2), (3, 4), (2, 3)]; - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let scc_nodes: HashSet> = strongly_connected_components(graph) - .groups() - .into_iter_groups() - .map(|(_, v)| v.name().into_iter_values().sorted().collect()) - .collect(); - - let expected: HashSet> = [vec!["2", "3", "4"], vec!["1"]] - .into_iter() - .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) - .collect(); - assert_eq!(scc_nodes, expected); - }); - } - - #[test] - fn scc_test_all_singletons() { - let graph = Graph::new(); - let edges = [ - (0, 1), - (1, 2), - (1, 3), - (2, 4), - (2, 5), - (3, 4), - (3, 5), - (4, 6), - ]; - for (src, dst) in edges { - graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let scc_nodes: HashSet> = strongly_connected_components(graph) - .groups() - .into_iter_groups() - .map(|(_, v)| v.name().into_iter_values().sorted().collect()) - .collect(); - - let expected: HashSet> = [ - vec!["0"], - vec!["1"], - vec!["2"], - vec!["3"], - vec!["4"], - vec!["5"], - vec!["6"], - ] - .into_iter() - .map(|v| v.into_iter().map(|s| s.to_owned()).collect()) - .collect(); - assert_eq!(scc_nodes, expected); - }); - } - } -} diff --git a/raphtory/tests/cached_view.rs b/raphtory/tests/cached_view.rs deleted file mode 100644 index 7dcefa302e..0000000000 --- a/raphtory/tests/cached_view.rs +++ /dev/null @@ -1,378 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use proptest::prelude::*; - use raphtory::{ - algorithms::motifs::triangle_count::triangle_count, db::graph::graph::assert_graph_equal, - prelude::*, test_storage, - }; - use raphtory_api::core::storage::timeindex::AsTime; - - #[test] - fn empty_graph() { - let graph = Graph::new(); - test_storage!(&graph, |graph| { - let sg = graph.cache_view(); - assert_graph_equal(&sg, &graph); - }); - } - - #[test] - fn empty_window() { - let graph = Graph::new(); - graph.add_edge(1, 1, 1, NO_PROPS, None).unwrap(); - test_storage!(&graph, |graph| { - let window = graph.window(2, 3); - let sg = window.cache_view(); - assert_graph_equal(&window, &sg); - }); - } - - #[test] - fn test_materialize_no_edges() { - let graph = Graph::new(); - - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 2, NO_PROPS, None, None).unwrap(); - - test_storage!(&graph, |graph| { - let sg = graph.cache_view(); - - let actual = sg.materialize().unwrap().into_events().unwrap(); - assert_graph_equal(&actual, &sg); - }); - } - - #[test] - fn test_mask_the_window_50pc() { - let graph = Graph::new(); - let edges = vec![ - (1, 2, 1), - (1, 3, 2), - (1, 4, 3), - (3, 1, 4), - (3, 4, 5), - (3, 5, 6), - (4, 5, 7), - (5, 6, 8), - (5, 8, 9), - (7, 5, 10), - (8, 5, 11), - (1, 9, 12), - (9, 1, 13), - (6, 3, 14), - (4, 8, 15), - (8, 3, 16), - (5, 10, 17), - (10, 5, 18), - (10, 8, 19), - (1, 11, 20), - (11, 1, 21), - (9, 11, 22), - (11, 9, 23), - ]; - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let window = graph.window(12, 24); - let mask = window.cache_view(); - let ts = triangle_count(&mask, None); - let tg = triangle_count(&window, None); - assert_eq!(ts, tg); - }); - } - - #[test] - fn masked_always_equals() { - fn check(edge_list: &[(u8, u8, i16, u8)]) { - let graph = Graph::new(); - for (src, dst, ts, layer) in edge_list { - graph - .add_edge( - *ts as i64, - *src as u64, - *dst as u64, - NO_PROPS, - Some(&layer.to_string()), - ) - .unwrap(); - } - - test_storage!(&graph, |graph| { - let layers = graph - .unique_layers() - .take(graph.unique_layers().count() / 2) - .collect_vec(); - - let earliest = graph.earliest_time().unwrap().t(); - let latest = graph.latest_time().unwrap().t(); - let middle = earliest + (latest - earliest) / 2; - - if !layers.is_empty() && earliest < middle && middle < latest { - let subgraph = graph.layers(layers).unwrap().window(earliest, middle); - let masked = subgraph.cache_view(); - assert_graph_equal(&subgraph, &masked); - } - }); - } - - proptest!(|(edge_list in any::>().prop_filter("greater than 3",|v| !v.is_empty() ))| { - check(&edge_list); - }) - } - - #[cfg(test)] - mod test_filters_cached_view { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::GraphTransformer, - views::{cached_view::CachedView, window_graph::WindowedGraph}, - }, - }, - prelude::{GraphViewOps, TimeOps}, - }; - use std::ops::Range; - - struct CachedGraphTransformer; - - impl GraphTransformer for CachedGraphTransformer { - type Return = CachedView; - fn apply(&self, graph: G) -> Self::Return { - graph.cache_view() - } - } - - struct WindowedCachedGraphTransformer(Range); - - impl GraphTransformer for WindowedCachedGraphTransformer { - type Return = WindowedGraph>; - fn apply(&self, graph: G) -> Self::Return { - graph.cache_view().window(self.0.start, self.0.end) - } - } - - mod test_nodes_filters_cached_view_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - TestGraphVariants, TestVariants, - }, - views::filter::model::{ - node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, - }, - }, - }, - prelude::AdditionOps, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - use crate::test::test_filters_cached_view::{ - CachedGraphTransformer, WindowedCachedGraphTransformer, - }; - - fn init_graph(graph: G) -> G { - let node_data = vec![ - (6, "N1", 2u64, "air_nomad"), - (7, "N1", 1u64, "air_nomad"), - (6, "N2", 1u64, "water_tribe"), - (7, "N2", 2u64, "water_tribe"), - (8, "N3", 1u64, "air_nomad"), - (9, "N4", 1u64, "air_nomad"), - (5, "N5", 1u64, "air_nomad"), - (6, "N5", 2u64, "air_nomad"), - (5, "N6", 1u64, "fire_nation"), - (6, "N6", 1u64, "fire_nation"), - (3, "N7", 1u64, "air_nomad"), - (5, "N7", 1u64, "air_nomad"), - (3, "N8", 1u64, "fire_nation"), - (4, "N8", 2u64, "fire_nation"), - ]; - - for (ts, name, value, kind) in node_data { - graph - .add_node(ts, name, [("p1", Prop::U64(value))], Some(kind), None) - .unwrap(); - } - - graph - } - - #[test] - fn test_nodes_filters() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - CachedGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_graph, - CachedGraphTransformer, - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_w() { - // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_pg_w() { - let filter = NodeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2", "N5", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - - mod test_edges_filter_cached_view_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestVariants, - }, - }, - prelude::{AdditionOps, EdgeFilter}, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - use crate::test::test_filters_cached_view::{ - CachedGraphTransformer, WindowedCachedGraphTransformer, - }; - use raphtory::db::graph::views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }; - - fn init_graph(graph: G) -> G { - let edge_data = vec![ - (6, "N1", "N2", 2u64), - (7, "N1", "N2", 1u64), - (6, "N2", "N3", 1u64), - (7, "N2", "N3", 2u64), - (8, "N3", "N4", 1u64), - (9, "N4", "N5", 1u64), - (5, "N5", "N6", 1u64), - (6, "N5", "N6", 2u64), - (5, "N6", "N7", 1u64), - (6, "N6", "N7", 1u64), - (3, "N7", "N8", 1u64), - (5, "N7", "N8", 1u64), - (3, "N8", "N1", 1u64), - (4, "N8", "N1", 2u64), - ]; - - for (ts, src, dst, p1_val) in edge_data { - graph - .add_edge(ts, src, dst, [("p1", Prop::U64(p1_val))], None) - .unwrap(); - } - - graph - } - - #[test] - fn test_edges_filters() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - CachedGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - CachedGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_filter_w() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_w() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; - assert_filter_edges_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowedCachedGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - } -} diff --git a/raphtory/tests/db_tests.rs b/raphtory/tests/db_tests.rs deleted file mode 100644 index c0e4e77d1b..0000000000 --- a/raphtory/tests/db_tests.rs +++ /dev/null @@ -1,3901 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use bigdecimal::BigDecimal; - use chrono::NaiveDateTime; - use itertools::Itertools; - use proptest::{arbitrary::any, prop_assert, prop_assert_eq, proptest, sample::subsequence}; - #[cfg(feature = "proto")] - use raphtory::serialise::StableDecode; - use raphtory::{ - algorithms::{ - centrality::{degree_centrality::degree_centrality, pagerank::unweighted_page_rank}, - components::weakly_connected_components, - }, - db::{ - api::{ - properties::internal::InternalMetadataOps, - state::MergePriority, - view::{ - internal::{GraphTimeSemanticsOps, InternalEdgeFilterOps}, - EdgeViewOps, LayerOps, NodeViewOps, TimeOps, - }, - }, - graph::{ - edge::EdgeView, edges::Edges, graph::assert_graph_equal, path::PathFromNode, - views::deletion_graph::PersistentGraph, - }, - }, - errors::GraphError, - graphgen::random_attachment::random_attachment, - prelude::*, - test_storage, - test_utils::{ - build_graph, build_graph_strat, EdgeFixture, EdgeUpdatesFixture, GraphFixture, - NodeFixture, PropUpdatesFixture, - }, - }; - use raphtory_api::core::{ - entities::{LayerId, GID, VID}, - storage::{ - arc_str::{ArcStr, OptionAsStr}, - timeindex::{AsTime, EventTime}, - }, - utils::{ - logging::global_info_logger, - time::{ParseTimeError, TryIntoTime, TryIntoTimeNeedsEventId}, - }, - }; - use raphtory_storage::{core_ops::CoreGraphOps, mutation::addition_ops::InternalAdditionOps}; - use rayon::{join, prelude::*}; - use std::{ - collections::{HashMap, HashSet}, - ops::{Deref, Range}, - sync::Arc, - }; - #[cfg(feature = "proto")] - use tempfile::TempDir; - use tracing::{error, info}; - - #[test] - fn edge_metadata() -> Result<(), GraphError> { - let g = Graph::new(); - - g.add_edge(0, 0, 0, NO_PROPS, None)?; - g.add_edge(0, 0, 1, NO_PROPS, None)?; - - g.edge(0, 0).unwrap().update_metadata( - vec![("x".to_string(), Prop::map([("n", Prop::U64(23))]))], - None, - )?; - g.edge(0, 1).unwrap().update_metadata( - vec![( - "a".to_string(), - Prop::map([("a", Prop::U8(1)), ("b", Prop::str("baa"))]), - )], - None, - )?; - - let e1 = g.edge(0, 0).unwrap(); - let actual = e1.metadata().as_map(); - assert_eq!(actual.get("x"), Some(&Prop::map([("n", Prop::U64(23))]))); - - let e2 = g.edge(0, 1).unwrap(); - let actual = e2.metadata().as_vec(); - assert_eq!( - actual, - vec![( - "a".into(), - Prop::map([("b", Prop::str("baa")), ("a", Prop::U8(1))]) - )] - ); - Ok(()) - } - - #[test] - fn test_empty_graph() { - let graph = Graph::new(); - test_storage!(&graph, |graph| { - assert!(!graph.has_edge(1, 2)); - - let test_time = 42; - let result = graph.at(test_time); - assert!(result.start.is_some()); - assert!(result.end.is_some()); - - let result = graph.after(test_time); - assert!(result.start.is_some()); - assert!(result.end.is_none()); - - let result = graph.before(test_time); - assert!(result.start.is_none()); - assert!(result.end.is_some()); - - assert_eq!( - graph.metadata_keys().collect::>(), - Vec::::new() - ); - assert_eq!( - graph.metadata_ids().collect::>(), - Vec::::new() - ); - assert_eq!( - graph.metadata_values().collect::>(), - Vec::>::new() - ); - assert!(graph.metadata().get_by_id(1).is_none()); - assert!(graph.get_metadata_id("1").is_none()); - assert!(graph.get_metadata(1).is_none()); - assert_eq!(graph.count_nodes(), 0); - assert_eq!(graph.count_edges(), 0); - assert_eq!(graph.count_temporal_edges(), 0); - - assert!(graph.start().is_none()); - assert!(graph.end().is_none()); - assert_eq!(graph.earliest_time(), None); - // assert!(graph.timeline_end().is_none()); - - assert!(graph.is_empty()); - - assert!(graph.nodes().collect().is_empty()); - assert_eq!(graph.edges().collect(), Vec::>::new()); - assert!(!graph.internal_edge_filtered()); - assert!(graph.edge(1, 2).is_none()); - assert!(graph.latest_time_global().is_none()); - assert!(graph - .latest_time_window(EventTime::start(1), EventTime::start(2)) - .is_none()); - assert!(graph.latest_time().is_none()); - assert!(graph.latest_time_global().is_none()); - assert!(graph.earliest_time_global().is_none()); - }); - } - - #[test] - fn test_multithreaded_add_edge() { - proptest!(|(edges: Vec<(u64, u64)>)| { - let g = Graph::new(); - edges.par_iter().enumerate().for_each(|(t, (i, j))| { - g.add_edge(t as i64, *i, *j, NO_PROPS, None).unwrap(); - }); - prop_assert!(edges.iter().all(|(i, j)| g.has_edge(*i, *j)) && g.count_temporal_edges() == edges.len()); - }); - } - - #[test] - fn test_multithreaded_add_edge_both_directions() { - proptest!(|(edges: Vec<(u64, u64)>)| { - let g = Graph::new(); - let mut self_loop_count = 0; - for (src, dst) in edges.iter() { - if src == dst { - self_loop_count += 1; - } - // try to maximise the chance that both directions of the edge are added in parallel - join(|| { - g.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); - }, || {g.add_edge(0, *dst, *src, NO_PROPS, None).unwrap();}); - } - - prop_assert!(edges.iter().all(|(i, j)| g.has_edge(*i, *j) && g.has_edge(*j, *i)) && g.count_temporal_edges() == 2*edges.len()-self_loop_count); - }); - } - - #[test] - fn add_node_grows_graph_len() { - proptest!(|(vs: Vec<(i64, u64)>)| { - let g = Graph::new(); - - let expected_len = vs.iter().map(|(_, v)| v).sorted().dedup().count(); - for (t, v) in vs { - g.add_node(t, v, NO_PROPS, None, None) - .map_err(|err| error!("{:?}", err)) - .ok(); - } - - prop_assert_eq!(g.count_nodes(), expected_len); - }); - } - - #[test] - fn add_node_gets_names() { - proptest!(|(vs: Vec)| { - global_info_logger(); - let g = Graph::new(); - - let expected_len = vs.iter().sorted().dedup().count(); - for (t, name) in vs.iter().enumerate() { - g.add_node(t as i64, name.clone(), NO_PROPS, None, None) - .map_err(|err| info!("{:?}", err)) - .ok(); - } - - prop_assert_eq!(g.count_nodes(), expected_len); - - let res = vs.iter().all(|name| { - let v = g.node(name.clone()).unwrap(); - v.name() == name.clone() - }); - prop_assert!(res); - }); - } - - #[test] - fn add_edge_grows_graph_edge_len() { - proptest!(|(edges: Vec<(i64, u64, u64)>)| { - let g = Graph::new(); - - let unique_nodes_count = edges - .iter() - .flat_map(|(_, src, dst)| vec![src, dst]) - .sorted() - .dedup() - .count(); - - let unique_edge_count = edges - .iter() - .map(|(_, src, dst)| (src, dst)) - .unique() - .count(); - - for (t, src, dst) in edges { - g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - - prop_assert_eq!(g.count_nodes(), unique_nodes_count); - prop_assert_eq!(g.count_edges(), unique_edge_count); - }); - } - - #[test] - fn simple_add_edge() { - let edges = vec![(1, 1, 2), (2, 2, 3), (3, 3, 4)]; - - let g = Graph::new(); - for &(t, src, dst) in edges.iter() { - g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - - assert!(edges.iter().all(|&(_, src, dst)| g.has_edge(src, dst))) - } - - #[test] - fn add_edge_works() { - proptest!(|(edges: Vec<(i64, u64, u64)>)| { - let g = Graph::new(); - for &(t, src, dst) in edges.iter() { - g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - - prop_assert!(edges.iter().all(|&(_, src, dst)| g.has_edge(src, dst))); - }); - } - - #[test] - fn get_edge_works() { - proptest!(|(edges: Vec<(i64, u64, u64)>)| { - let g = Graph::new(); - for &(t, src, dst) in edges.iter() { - g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - - prop_assert!(edges - .iter() - .all(|&(_, src, dst)| g.edge(src, dst).is_some())); - }); - } - - #[test] - fn import_from_another_graph() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - - assert_eq!(g_b.history(), vec![1]); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let gg = Graph::new(); - let res = gg.import_node(&g_a, false).unwrap(); - assert_eq!(res.name(), "A"); - assert_eq!(res.history(), vec![0]); - let res = gg.import_node(&g_b, false).unwrap(); - assert_eq!(res.name(), "B"); - assert_eq!(res.history(), vec![1]); - assert_eq!(res.properties().get("temp").unwrap(), Prop::Bool(true)); - assert_eq!(res.metadata().get("con").unwrap(), Prop::I64(11)); - - let gg = Graph::new(); - gg.add_node(1, "B", NO_PROPS, None, None).unwrap(); - let res = gg.import_nodes(vec![&g_a, &g_b], false); - match res { - Err(GraphError::NodesExistError(ids)) => { - assert_eq!( - ids.into_iter() - .map(|id| id.to_string()) - .collect::>(), - vec!["B"], - ); - } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - - assert_eq!(gg.node("A"), None); - - let gg = Graph::new(); - gg.import_nodes(vec![&g_a, &g_b], false).unwrap(); - assert_eq!(gg.nodes().name().collect_vec(), vec!["A", "B"]); - - let e_a_b = g.add_edge(2, "A", "B", NO_PROPS, None).unwrap(); - let res = gg.import_edge(&e_a_b, false).unwrap(); - assert_eq!( - (res.src().name(), res.dst().name()), - (e_a_b.src().name(), e_a_b.dst().name()) - ); - let e_a_b_p = g - .add_edge( - 3, - "A", - "B", - vec![("etemp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); - let gg = Graph::new(); - let _ = gg.add_node(0, "B", NO_PROPS, None, None); - let res = gg.import_edge(&e_a_b_p, false).expect("Failed to add edge"); - assert_eq!(res.properties().as_vec(), e_a_b_p.properties().as_vec()); - - let e_c_d = g.add_edge(4, "C", "D", NO_PROPS, None).unwrap(); - - let gg = Graph::new(); - gg.import_edges(vec![&e_a_b, &e_c_d], false).unwrap(); - assert_eq!(gg.edges().len(), 2); - - let gg = Graph::new(); - gg.add_edge(1, "C", "D", NO_PROPS, None).unwrap(); - let res = gg.import_edges(vec![&e_a_b, &e_c_d], false); - match res { - Err(GraphError::EdgesExistError(duplicates)) => { - assert_eq!( - duplicates - .into_iter() - .map(|(x, y)| (x.to_string(), y.to_string())) - .collect::>(), - vec![("C".to_string(), "D".to_string())] - ); - } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - assert_eq!(gg.edge("A", "B"), None); - } - - #[test] - fn import_node_as() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - - let gg = Graph::new(); - let res = gg.import_node_as(&g_a, "X", false).unwrap(); - assert_eq!(res.name(), "X"); - assert_eq!(res.history(), vec![0]); - - let _ = gg.add_node(1, "Y", NO_PROPS, None, None).unwrap(); - let res = gg.import_node_as(&g_b, "Y", false); - match res { - Err(GraphError::NodeExistsError(id)) => { - assert_eq!(id.to_string(), "Y"); - } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["X", "Y"]); // Nodes up until first failure are imported - let y = gg.node("Y").unwrap(); - - assert_eq!(y.name(), "Y"); - assert_eq!(y.history().t(), vec![1]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); - } - - #[test] - fn import_node_as_merge() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - - let gg = Graph::new(); - gg.add_node(1, "Y", NO_PROPS, None, None).unwrap(); - - let res = gg.import_node_as(&g_a, "X", false).unwrap(); - assert_eq!(res.name(), "X"); - assert_eq!(res.history(), vec![0]); - - let res = gg.import_node_as(&g_b, "Y", true).unwrap(); - assert_eq!(res.name(), "Y"); - assert_eq!(res.history(), vec![1, 1]); - assert_eq!(res.properties().get("temp").unwrap(), Prop::Bool(true)); - assert_eq!(res.metadata().get("con").unwrap(), Prop::I64(11)); - } - - #[test] - fn import_nodes_as() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let g_c = g.add_node(0, "C", NO_PROPS, None, None).unwrap(); - - let gg = Graph::new(); - gg.add_node(1, "Q", NO_PROPS, None, None).unwrap(); - gg.add_node(1, "R", NO_PROPS, None, None).unwrap(); - let res = gg.import_nodes_as(vec![&g_a, &g_b, &g_c], vec!["P", "Q", "R"], false); - match res { - Err(GraphError::NodesExistError(ids)) => { - assert_eq!( - ids.into_iter() - .map(|id| id.to_string()) - .collect::>(), - vec!["Q", "R"], - ); - } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["Q", "R"]); // Nodes up until first failure are imported - let y = gg.node("Q").unwrap(); - assert_eq!(y.name(), "Q"); - assert_eq!(y.history(), vec![1]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); - } - - #[test] - fn import_nodes_as_merge() { - let g = Graph::new(); - let g_a = g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - - let gg = Graph::new(); - gg.add_node(1, "Q", NO_PROPS, None, None).unwrap(); - gg.import_nodes_as(vec![&g_a, &g_b], vec!["P", "Q"], true) - .unwrap(); - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["P", "Q"]); - let y = gg.node("Q").unwrap(); - assert_eq!(y.name(), "Q"); - assert_eq!(y.history().t().collect(), vec![1, 1]); - assert_eq!(y.properties().get("temp").unwrap(), Prop::Bool(true)); - assert_eq!(y.metadata().get("con").unwrap(), Prop::I64(11)); - } - - #[test] - fn import_edge_as() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]) - .unwrap(); - let e_a_b = g - .add_edge( - 2, - "A", - "B", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); - let e_b_c = g - .add_edge( - 2, - "B", - "C", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); - - let gg = Graph::new(); - gg.add_edge(1, "X", "Y", NO_PROPS, None).unwrap(); - gg.import_edge_as(&e_b_c, ("Y", "Z"), false).unwrap(); - let res = gg.import_edge_as(&e_a_b, ("X", "Y"), false); - match res { - Err(GraphError::EdgeExistsError(src_id, dst_id)) => { - assert_eq!(src_id.to_string(), "X"); - assert_eq!(dst_id.to_string(), "Y"); - } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["X", "Y", "Z"]); - let x = gg.node("X").unwrap(); - assert_eq!(x.name(), "X"); - assert_eq!(x.history(), vec![1]); - let y = gg.node("Y").unwrap(); - assert_eq!(y.name(), "Y"); - assert_eq!(y.history(), vec![1, 2]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); - - let e_src = gg.edge("X", "Y").unwrap().src().name(); - let e_dst = gg.edge("X", "Y").unwrap().dst().name(); - assert_eq!(e_src, "X"); - assert_eq!(e_dst, "Y"); - - let props = gg.edge("X", "Y").unwrap().properties().as_vec(); - assert_eq!(props, vec![]); - } - - #[test] - fn import_edge_as_merge() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let e_a_b = g - .add_edge( - 2, - "A", - "B", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); - - let gg = Graph::new(); - let _ = gg.add_edge(3, "X", "Y", NO_PROPS, None).unwrap(); - let res = gg.import_edge_as(&e_a_b, ("X", "Y"), true).unwrap(); - assert_eq!(res.src().name(), "X"); - assert_eq!(res.dst().name(), "Y"); - assert_eq!(res.properties().as_vec(), e_a_b.properties().as_vec()); - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["X", "Y"]); - let x = gg.node("X").unwrap(); - assert_eq!(x.name(), "X"); - assert_eq!(x.history(), vec![2, 3]); - let y = gg.node("Y").unwrap(); - assert_eq!(y.name(), "Y"); - assert_eq!(y.history(), vec![2, 3]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); - } - - #[test] - fn import_edges_as() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]) - .unwrap(); - g.add_node(0, "C", NO_PROPS, None, None).unwrap(); - let e_a_b = g - .add_edge( - 2, - "A", - "B", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); - let e_b_c = g.add_edge(2, "B", "C", NO_PROPS, None).unwrap(); - - let gg = Graph::new(); - gg.add_edge(1, "Y", "Z", NO_PROPS, None).unwrap(); - let res = gg.import_edges_as([&e_a_b, &e_b_c], [("X", "Y"), ("Y", "Z")], false); - match res { - Err(GraphError::EdgesExistError(duplicates)) => { - assert_eq!( - duplicates - .into_iter() - .map(|(x, y)| (x.to_string(), y.to_string())) - .collect::>(), - vec![("Y".to_string(), "Z".to_string())] - ); - } - Err(e) => panic!("Unexpected error: {:?}", e), - Ok(_) => panic!("Expected error but got Ok"), - } - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["Y", "Z"]); - let y = gg.node("Y").unwrap(); - assert_eq!(y.name(), "Y"); - assert_eq!(y.history(), vec![1]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); - let x = gg.node("Z").unwrap(); - assert_eq!(x.name(), "Z"); - assert_eq!(x.history(), vec![1]); - - assert!(gg.edge("X", "Y").is_none()); - - let e_y_z = gg.edge("Y", "Z").unwrap(); - assert_eq!( - (e_y_z.src().name().as_str(), e_y_z.dst().name().as_str()), - ("Y", "Z") - ); - - let props = e_y_z.properties().as_vec(); - assert_eq!(props, vec![]); - } - - #[test] - fn import_edges_as_merge() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, None, None).unwrap(); - let g_b = g - .add_node( - 1, - "B", - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - let _ = g_b.add_metadata(vec![("con".to_string(), Prop::I64(11))]); - let e_a_b = g - .add_edge( - 2, - "A", - "B", - vec![("e_temp".to_string(), Prop::Bool(false))], - None, - ) - .unwrap(); - - let gg = Graph::new(); - gg.add_edge(3, "X", "Y", NO_PROPS, None).unwrap(); - gg.import_edges_as([&e_a_b], [("X", "Y")], true).unwrap(); - - let e_x_y = gg.edge("X", "Y").unwrap(); - assert_eq!( - (e_x_y.src().name().as_str(), e_x_y.dst().name().as_str()), - ("X", "Y") - ); - assert_eq!(e_x_y.properties().get("e_temp").unwrap(), Prop::Bool(false)); - - let mut nodes = gg.nodes().name().collect_vec(); - nodes.sort(); - assert_eq!(nodes, vec!["X", "Y"]); - let x = gg.node("X").unwrap(); - assert_eq!(x.name(), "X"); - assert_eq!(x.history(), vec![2, 3]); - let y = gg.node("Y").unwrap(); - assert_eq!(y.name(), "Y"); - assert_eq!(y.history(), vec![2, 3]); - assert_eq!(y.properties().get("temp"), None); - assert_eq!(y.metadata().get("con"), None); - } - - #[test] - fn props_with_layers() { - global_info_logger(); - let g = Graph::new(); - g.add_edge(0, "A", "B", NO_PROPS, None).unwrap(); - let ed = g.edge("A", "B").unwrap(); - ed.add_metadata(vec![("CCC", Prop::str("RED"))], None) - .unwrap(); - info!("{:?}", ed.metadata().as_map()); - g.add_edge(0, "A", "B", NO_PROPS, Some("LAYERONE")).unwrap(); - ed.add_metadata(vec![("CCC", Prop::str("BLUE"))], Some("LAYERONE")) - .unwrap(); - info!("{:?}", ed.metadata().as_map()); - } - - #[test] - #[cfg(feature = "proto")] - fn graph_save_to_load_from_file() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let g = Graph::new(); - - for (t, src, dst) in &vs { - g.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } - - let tmp_raphtory_path: TempDir = TempDir::new().unwrap(); - - let graph_path = format!("{}/graph.bin", tmp_raphtory_path.path().display()); - g.encode(&graph_path).unwrap(); - - // Load from files - let g2 = Graph::decode(&graph_path).unwrap(); - - assert_eq!(g, g2); - } - - #[test] - fn has_edge() { - let g = Graph::new(); - g.add_edge(1, 7, 8, NO_PROPS, None).unwrap(); - - assert!(!g.has_edge(8, 7)); - assert!(g.has_edge(7, 8)); - - g.add_edge(1, 7, 9, NO_PROPS, None).unwrap(); - - assert!(!g.has_edge(9, 7)); - assert!(g.has_edge(7, 9)); - } - - #[test] - fn has_edge_str() { - let g = Graph::new(); - g.add_edge(2, "haaroon", "northLondon", NO_PROPS, None) - .unwrap(); - assert!(g.has_edge("haaroon", "northLondon")); - } - - #[test] - fn graph_edge() { - let graph = Graph::new(); - let es = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - for (t, src, dst) in es { - graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let e = graph - .window(i64::MIN, i64::MAX) - .layers(Layer::Default) - .unwrap() - .edge(1, 3) - .unwrap(); - - assert_eq!(e.src().id().into_u64(), Some(1u64)); - assert_eq!(e.dst().id().into_u64(), Some(3u64)); - }); - } - - #[test] - fn graph_degree_window() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } - - let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; - let actual = (1..=3) - .map(|i| { - let v = graph.node(i).unwrap(); - ( - v.window(-1, 7).in_degree(), - v.window(1, 7).out_degree(), - v.window(0, 1).degree(), - ) - }) - .collect::>(); - - assert_eq!(actual, expected); - } - - #[test] - fn graph_edges_window() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } - let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; - let actual = (1..=3) - .map(|i| { - let v = graph.node(i).unwrap(); - ( - v.window(-1, 7).in_edges().iter().count(), - v.window(1, 7).out_edges().iter().count(), - v.window(0, 1).edges().iter().count(), - ) - }) - .collect::>(); - - assert_eq!(actual, expected); - } - - #[test] - fn test_explode_layers_time() { - global_info_logger(); - let g = Graph::new(); - g.add_edge( - 1, - 1, - 2, - vec![("duration".to_string(), Prop::U32(5))], - Some("a"), - ) - .map_err(|err| error!("{:?}", err)) - .ok(); - g.add_edge( - 2, - 1, - 2, - vec![("duration".to_string(), Prop::U32(5))], - Some("a"), - ) - .map_err(|err| error!("{:?}", err)) - .ok(); - g.add_edge( - 3, - 1, - 2, - vec![("duration".to_string(), Prop::U32(5))], - Some("a"), - ) - .map_err(|err| error!("{:?}", err)) - .ok(); - g.add_edge( - 4, - 1, - 2, - vec![("duration".to_string(), Prop::U32(6))], - Some("b"), - ) - .map_err(|err| error!("{:?}", err)) - .ok(); - g.add_edge(5, 1, 2, NO_PROPS, Some("c")) - .map_err(|err| error!("{:?}", err)) - .ok(); - - assert_eq!(g.latest_time().unwrap().t(), 5); - - let earliest_times = g - .edge(1, 2) - .unwrap() - .explode_layers() - .earliest_time() - .map(|t| t.unwrap().t()) - .collect_vec(); - - assert_eq!(earliest_times, vec![1, 4, 5]); - - let latest_times = g - .edge(1, 2) - .unwrap() - .explode_layers() - .latest_time() - .map(|t| t.unwrap().t()) - .collect_vec(); - - assert_eq!(latest_times, vec![3, 4, 5]); - } - - #[test] - fn time_test() { - global_info_logger(); - let g = Graph::new(); - - assert_eq!(g.latest_time(), None); - assert_eq!(g.earliest_time(), None); - - g.add_node(5, 1, NO_PROPS, None, None) - .map_err(|err| error!("{:?}", err)) - .ok(); - - assert_eq!(g.latest_time().unwrap().t(), 5); - assert_eq!(g.earliest_time().unwrap().t(), 5); - - let g = Graph::new(); - - g.add_edge(10, 1, 2, NO_PROPS, None).unwrap(); - assert_eq!(g.latest_time().unwrap().t(), 10); - assert_eq!(g.earliest_time().unwrap().t(), 10); - - g.add_node(5, 1, NO_PROPS, None, None) - .map_err(|err| error!("{:?}", err)) - .ok(); - assert_eq!(g.latest_time().unwrap().t(), 10); - assert_eq!(g.earliest_time().unwrap().t(), 5); - - g.add_edge(20, 3, 4, NO_PROPS, None).unwrap(); - assert_eq!(g.latest_time().unwrap().t(), 20); - assert_eq!(g.earliest_time().unwrap().t(), 5); - - random_attachment(&g, 100, 10, None); - assert_eq!(g.latest_time().unwrap().t(), 126); - assert_eq!(g.earliest_time().unwrap().t(), 5); - } - - #[test] - fn test_metadata_props() { - let g = Graph::new(); - let n = g.add_node(1, 1, [("p1", 1)], None, None).unwrap(); - n.add_metadata([("m1", 1)]).unwrap(); - let n = g.add_node(1, 2, [("p2", 2)], None, None).unwrap(); - n.add_metadata([("m2", 2)]).unwrap(); - - let n1_meta = g - .node(1) - .unwrap() - .metadata() - .keys() - .map(|s| s.to_string()) - .collect::>(); - assert_eq!(n1_meta, vec!["m1", "m2"]); - let n1_props = g - .node(1) - .unwrap() - .properties() - .keys() - .map(|s| s.to_string()) - .collect::>(); - assert_eq!(n1_props, vec!["p1", "p2"]); - } - - #[test] - fn metadata() { - let g = Graph::new(); - g.add_edge(0, 11, 22, NO_PROPS, None).unwrap(); - g.add_edge( - 0, - 11, - 11, - vec![("temp".to_string(), Prop::Bool(true))], - None, - ) - .unwrap(); - g.add_edge(0, 22, 33, NO_PROPS, None).unwrap(); - g.add_edge(0, 33, 11, NO_PROPS, None).unwrap(); - g.add_node( - 0, - 11, - vec![("temp".to_string(), Prop::Bool(true))], - None, - None, - ) - .unwrap(); - g.add_edge(0, 44, 55, NO_PROPS, None).unwrap(); - let v11 = g.node(11).unwrap(); - let v22 = g.node(22).unwrap(); - let v33 = g.node(33).unwrap(); - let v44 = g.node(44).unwrap(); - let v55 = g.node(55).unwrap(); - let edge1111 = g.edge(&v11, &v11).unwrap(); - let edge2233 = g.edge(&v22, &v33).unwrap(); - let edge3311 = g.edge(&v33, &v11).unwrap(); - - v11.add_metadata(vec![("a", Prop::U64(11)), ("b", Prop::I64(11))]) - .unwrap(); - v11.add_metadata(vec![("c", Prop::U32(11))]).unwrap(); - - v44.add_metadata(vec![("e", Prop::U8(1))]).unwrap(); - v55.add_metadata(vec![("f", Prop::U16(1))]).unwrap(); - edge1111 - .add_metadata(vec![("d", Prop::U64(1111))], None) - .unwrap(); - edge3311 - .add_metadata(vec![("a", Prop::U64(3311))], None) - .unwrap(); - - // cannot add properties to non-existant layer - assert!(edge1111 - .add_metadata([("test", "test")], Some("test")) - .is_err()); - - // cannot change property type - assert!(v22.add_metadata(vec![("b", Prop::U64(22))]).is_err()); - - // TODO: Revisit this test after metadata handling is finalised. - // Refer to the `test_metadata_props` test for context. - // assert_eq!( - // v11.metadata().keys().collect::>(), - // vec!["a", "b", "c"] - // ); - // assert!(v22.metadata().keys().next().is_none()); - // assert!(v33.metadata().keys().next().is_none()); - // assert_eq!(v44.metadata().keys().collect::>(), vec!["e"]); - // assert_eq!(v55.metadata().keys().collect::>(), vec!["f"]); - - assert_eq!( - edge1111.metadata().keys().collect::>(), - vec!["d", "a"] // all edges get all ids anyhow - ); - assert_eq!( - edge3311.metadata().keys().collect::>(), - vec!["d", "a"] - ); - assert_eq!( - edge2233.metadata().keys().collect::>(), - vec!["d", "a"] - ); - - assert_eq!(v11.metadata().get("a"), Some(Prop::U64(11))); - assert_eq!(v11.metadata().get("b"), Some(Prop::I64(11))); - assert_eq!(v11.metadata().get("c"), Some(Prop::U32(11))); - assert_eq!(v22.metadata().get("b"), None); - assert_eq!(v44.metadata().get("e"), Some(Prop::U8(1))); - assert_eq!(v55.metadata().get("f"), Some(Prop::U16(1))); - assert_eq!(v22.metadata().get("a"), None); - assert_eq!(edge1111.metadata().get("d"), Some(Prop::U64(1111))); - assert_eq!(edge3311.metadata().get("a"), Some(Prop::U64(3311))); - assert_eq!(edge2233.metadata().get("a"), None); - - // cannot add properties to non-existant layer - assert!(edge1111 - .add_metadata([("test", "test")], Some("test")) - .is_err()); - g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); - // cannot add properties to layer without updates - assert!(edge1111 - .add_metadata([("test", "test")], Some("test")) - .is_err()); - } - - #[test] - fn temporal_node_rows_1_node() { - let graph = Graph::new(); - - graph - .add_node(0, 1, [("cool".to_string(), Prop::Bool(true))], None, None) - .unwrap(); - - test_storage!(&graph, |graph| { - let actual = graph - .node(1) - .unwrap() - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); - - assert_eq!(actual, vec![(0.into(), vec![Prop::Bool(true)])]); - }); - - graph - .add_node(0, 1, [("coolio".to_string(), Prop::U64(9))], None, None) - .unwrap(); - - test_storage!(&graph, |graph| { - let actual = graph - .node(1) - .unwrap() - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); - - assert_eq!( - actual, - vec![ - (EventTime::new(0, 0), vec![Prop::Bool(true)]), - (EventTime::new(0, 1), vec![Prop::U64(9)]) - ] - ); - }); - - graph - .add_node( - 1, - 1, - [ - ("cool".to_string(), Prop::Bool(false)), - ("coolio".to_string(), Prop::U64(19)), - ], - None, - None, - ) - .unwrap(); - - test_storage!(&graph, |graph| { - let actual = graph - .node(1) - .unwrap() - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); - - let expected = vec![ - (EventTime::new(0, 0), vec![Prop::Bool(true)]), - (EventTime::new(0, 1), vec![Prop::U64(9)]), - (EventTime::new(1, 2), vec![Prop::Bool(false), Prop::U64(19)]), - ]; - assert_eq!(actual, expected); - }); - - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - // edge additions should not show up in prop rows - graph.add_edge(3, 1, 1, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let actual = graph - .node(1) - .unwrap() - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); - - let expected = vec![ - (EventTime::new(0, 0), vec![Prop::Bool(true)]), - (EventTime::new(0, 1), vec![Prop::U64(9)]), - (EventTime::new(1, 2), vec![Prop::Bool(false), Prop::U64(19)]), - (EventTime::new(2, 3), vec![]), - ]; - - assert_eq!(actual, expected); - }); - } - - #[test] - fn temporal_node_rows_nodes() { - let graph = Graph::new(); - let mut nodes = Vec::new(); - nodes.push( - graph - .add_node(0, 1, [("cool".to_string(), Prop::U64(1))], None, None) - .unwrap() - .node, - ); - nodes.push( - graph - .add_node(1, 2, [("cool".to_string(), Prop::U64(2))], None, None) - .unwrap() - .node, - ); - nodes.push( - graph - .add_node(2, 3, [("cool".to_string(), Prop::U64(3))], None, None) - .unwrap() - .node, - ); - - let prop_ids: Arc<[usize]> = graph.node_meta().temporal_prop_mapper().ids().collect(); - for (id, n) in nodes.into_iter().enumerate() { - let actual = graph - .core_graph() - .nodes() - .node(n) - .temp_prop_rows(prop_ids.clone()) - .map(|(t, _, row)| (t, row.into_iter().map(|(_, p)| p).collect::>())) - .collect::>(); - - let expected = vec![( - EventTime::new(id as i64, id), - vec![Prop::U64((id as u64) + 1)], - )]; - assert_eq!(actual, expected); - } - } - - #[test] - fn temporal_node_rows_window() { - let graph = Graph::new(); - graph - .add_node(0, 1, [("cool".to_string(), Prop::U64(1))], None, None) - .unwrap(); - graph - .add_node(1, 1, [("cool".to_string(), Prop::U64(2))], None, None) - .unwrap(); - graph - .add_node(2, 1, [("cool".to_string(), Prop::U64(3))], None, None) - .unwrap(); - - test_storage!(&graph, |graph| { - let prop_ids: Arc<[usize]> = graph.node_meta().temporal_prop_mapper().ids().collect(); - let get_rows = |vid: VID, range: Range| { - graph - .core_graph() - .nodes() - .node(vid) - .temp_prop_rows_range(Some(range), prop_ids.clone()) - .map(|(t, _, row)| (t, row.into_iter().map(|(_, p)| p).collect::>())) - .collect::>() - }; - let actual = get_rows(VID(0), EventTime::new(2, 0)..EventTime::new(3, 0)); - - let expected = vec![(EventTime::new(2, 2), vec![Prop::U64(3)])]; - - assert_eq!(actual, expected); - - let actual = get_rows(VID(0), EventTime::new(0, 0)..EventTime::new(3, 0)); - let expected = vec![ - (EventTime::new(0, 0), vec![Prop::U64(1)]), - (EventTime::new(1, 1), vec![Prop::U64(2)]), - (EventTime::new(2, 2), vec![Prop::U64(3)]), - ]; - - assert_eq!(actual, expected); - }); - } - - #[test] - fn temporal_props_node() { - let graph = Graph::new(); - - graph - .add_node(0, 1, [("cool".to_string(), Prop::Bool(true))], None, None) - .unwrap(); - - let v = graph.node(1).unwrap(); - - let actual = v.properties().get("cool"); - assert_eq!(actual, Some(Prop::Bool(true))); - - // we flip cool from true to false after t 3 - graph - .add_node(3, 1, [("cool".to_string(), Prop::Bool(false))], None, None) - .unwrap(); - - test_storage!(&graph, |graph| { - let wg = graph.window(3, 15); - let v = wg.node(1).unwrap(); - - let actual = v.properties().get("cool"); - assert_eq!(actual, Some(Prop::Bool(false))); - - let hist: Vec<_> = v - .properties() - .temporal() - .get("cool") - .unwrap() - .iter() - .map(|(x, y)| (x.t(), y)) - .collect(); - assert_eq!(hist, vec![(3, Prop::Bool(false))]); - - let v = graph.node(1).unwrap(); - - let hist: Vec<_> = v - .properties() - .temporal() - .get("cool") - .unwrap() - .iter() - .map(|(x, y)| (x.t(), y)) - .collect(); - assert_eq!(hist, vec![(0, Prop::Bool(true)), (3, Prop::Bool(false))]); - }); - } - - #[test] - fn temporal_props_edge() { - let graph = Graph::new(); - - graph - .add_edge(1, 0, 1, vec![("distance".to_string(), Prop::U32(5))], None) - .expect("add edge"); - - test_storage!(&graph, |graph| { - let e = graph.edge(0, 1).unwrap(); - - let prop = e.properties().get("distance").unwrap(); - assert_eq!(prop, Prop::U32(5)); - }); - } - - #[test] - fn graph_neighbours_window() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let expected = vec![ - (vec![1, 2], vec![1, 2, 3], vec![1]), - (vec![1], vec![], vec![]), - (vec![1], vec![], vec![]), - ]; - let actual = (1..=3) - .map(|i| { - let v = graph.node(i).unwrap(); - ( - v.window(-1, 7) - .in_neighbours() - .id() - .filter_map(|id| id.as_u64()) - .collect::>(), - v.window(1, 7) - .out_neighbours() - .id() - .filter_map(|id| id.as_u64()) - .collect::>(), - v.window(0, 1) - .neighbours() - .id() - .filter_map(|id| id.as_u64()) - .collect::>(), - ) - }) - .collect::>(); - - assert_eq!(actual, expected); - }); - } - - #[test] - fn test_time_range_on_empty_graph() { - let graph = Graph::new(); - - test_storage!(&graph, |graph| { - let rolling = graph.rolling(1, None).unwrap().collect_vec(); - assert!(rolling.is_empty()); - - let expanding = graph.expanding(1).unwrap().collect_vec(); - assert!(expanding.is_empty()); - }); - } - - #[test] - fn test_add_node_with_nums() { - let graph = Graph::new(); - - graph.add_node(1, 831, NO_PROPS, None, None).unwrap(); - test_storage!(&graph, |graph| { - assert!(graph.has_node(831)); - - assert_eq!(graph.count_nodes(), 1); - }); - } - - #[test] - fn test_add_node_with_strings() { - let graph = Graph::new(); - - graph.add_node(0, "haaroon", NO_PROPS, None, None).unwrap(); - graph.add_node(1, "hamza", NO_PROPS, None, None).unwrap(); - test_storage!(&graph, |graph| { - assert!(graph.has_node("haaroon")); - assert!(graph.has_node("hamza")); - - assert_eq!(graph.count_nodes(), 2); - }); - } - - #[test] - fn layers() -> Result<(), GraphError> { - let graph = Graph::new(); - graph.add_edge(0, 11, 22, NO_PROPS, None)?; - graph.add_edge(0, 11, 33, NO_PROPS, None)?; - graph.add_edge(0, 33, 11, NO_PROPS, None)?; - graph.add_edge(0, 11, 22, NO_PROPS, Some("layer1"))?; - graph.add_edge(0, 11, 33, NO_PROPS, Some("layer2"))?; - graph.add_edge(0, 11, 44, NO_PROPS, Some("layer2"))?; - - assert!(graph.has_edge(11, 22)); - assert!(graph.default_layer().has_edge(11, 22)); - assert!(!graph.default_layer().has_edge(11, 44)); - assert!(!graph.layers("layer2")?.has_edge(11, 22)); - assert!(graph.layers("layer2")?.has_edge(11, 44)); - - assert!(graph.edge(11, 22).is_some()); - assert!(graph.layers(Layer::Default)?.edge(11, 44).is_none()); - assert!(graph.layers("layer2")?.edge(11, 22).is_none()); - assert!(graph.layers("layer2")?.edge(11, 44).is_some()); - - assert!(graph.exclude_layers("layer2")?.edge(11, 44).is_none()); - assert!(graph.exclude_layers("layer2")?.edge(11, 33).is_some()); - assert!(graph.exclude_layers("layer2")?.edge(11, 22).is_some()); - - let dft_layer = graph.default_layer(); - let layer1 = graph.layers("layer1")?; - let layer2 = graph.layers("layer2")?; - assert!(graph.layers("missing layer").is_err()); - - assert_eq!(graph.count_nodes(), 4); - assert_eq!(graph.count_edges(), 4); - assert_eq!(dft_layer.count_edges(), 3); - assert_eq!(layer1.count_edges(), 1); - assert_eq!(layer2.count_edges(), 2); - - let node = graph.node(11).unwrap(); - let node_dft = dft_layer.node(11).unwrap(); - let node1 = layer1.node(11).unwrap(); - let node2 = layer2.node(11).unwrap(); - - assert_eq!(node.degree(), 3); - assert_eq!(node_dft.degree(), 2); - assert_eq!(node1.degree(), 1); - assert_eq!(node2.degree(), 2); - - assert_eq!(node.out_degree(), 3); - assert_eq!(node_dft.out_degree(), 2); - assert_eq!(node1.out_degree(), 1); - assert_eq!(node2.out_degree(), 2); - - assert_eq!(node.in_degree(), 1); - assert_eq!(node_dft.in_degree(), 1); - assert_eq!(node1.in_degree(), 0); - assert_eq!(node2.in_degree(), 0); - - fn to_tuples<'graph, G: GraphViewOps<'graph>>(edges: Edges<'graph, G>) -> Vec<(u64, u64)> { - edges - .id() - .filter_map(|(s, d)| s.to_u64().zip(d.to_u64())) - .sorted() - .collect_vec() - } - - assert_eq!( - to_tuples(node.edges()), - vec![(11, 22), (11, 33), (11, 44), (33, 11)] - ); - assert_eq!( - to_tuples(node_dft.edges()), - vec![(11, 22), (11, 33), (33, 11)] - ); - assert_eq!(to_tuples(node1.edges()), vec![(11, 22)]); - assert_eq!(to_tuples(node2.edges()), vec![(11, 33), (11, 44)]); - - assert_eq!(to_tuples(node.in_edges()), vec![(33, 11)]); - assert_eq!(to_tuples(node_dft.in_edges()), vec![(33, 11)]); - assert_eq!(to_tuples(node1.in_edges()), vec![]); - assert_eq!(to_tuples(node2.in_edges()), vec![]); - - assert_eq!( - to_tuples(node.out_edges()), - vec![(11, 22), (11, 33), (11, 44)] - ); - assert_eq!(to_tuples(node_dft.out_edges()), vec![(11, 22), (11, 33)]); - assert_eq!(to_tuples(node1.out_edges()), vec![(11, 22)]); - assert_eq!(to_tuples(node2.out_edges()), vec![(11, 33), (11, 44)]); - - fn to_ids<'graph, G: GraphViewOps<'graph>>( - neighbours: PathFromNode<'graph, G>, - ) -> Vec { - neighbours - .iter() - .filter_map(|n| n.id().as_u64()) - .sorted() - .collect_vec() - } - - assert_eq!(to_ids(node.neighbours()), vec![22, 33, 44]); - assert_eq!(to_ids(node_dft.neighbours()), vec![22, 33]); - assert_eq!(to_ids(node1.neighbours()), vec![22]); - assert_eq!(to_ids(node2.neighbours()), vec![33, 44]); - - assert_eq!(to_ids(node.out_neighbours()), vec![22, 33, 44]); - assert_eq!(to_ids(node_dft.out_neighbours()), vec![22, 33]); - assert_eq!(to_ids(node1.out_neighbours()), vec![22]); - assert_eq!(to_ids(node2.out_neighbours()), vec![33, 44]); - - assert_eq!(to_ids(node.in_neighbours()), vec![33]); - assert_eq!(to_ids(node_dft.in_neighbours()), vec![33]); - assert!(to_ids(node1.in_neighbours()).is_empty()); - assert!(to_ids(node2.in_neighbours()).is_empty()); - Ok(()) - } - - #[test] - fn test_props() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("weight", Prop::I64(1))], None) - .unwrap(); - g.add_edge(1, 1, 2, [("weight", Prop::I64(2))], None) - .unwrap(); - g.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); - - let exploded = g.edge(1, 2).unwrap().explode(); - let res = exploded.properties().map(|p| p.as_vec()).collect_vec(); - assert_eq!( - res, - vec![ - vec![("weight".into(), Prop::I64(1))], - vec![("weight".into(), Prop::I64(2))], - vec![] - ] - ); - } - - #[test] - fn test_exploded_edge() { - let graph = Graph::new(); - graph - .add_edge(0, 1, 2, [("weight", Prop::I64(1))], None) - .unwrap(); - graph - .add_edge(1, 1, 2, [("weight", Prop::I64(2))], None) - .unwrap(); - graph - .add_edge(2, 1, 2, [("weight", Prop::I64(3))], None) - .unwrap(); - test_storage!(&graph, |graph| { - let exploded = graph.edge(1, 2).unwrap().explode(); - - let res = exploded.properties().map(|p| p.as_vec()).collect_vec(); - - let mut expected = Vec::new(); - for i in 1..4 { - expected.push(vec![("weight".into(), Prop::I64(i))]); - } - - assert_eq!(res, expected); - - let e = graph - .node(1) - .unwrap() - .edges() - .explode() - .properties() - .map(|p| p.as_vec()) - .collect_vec(); - assert_eq!(e, expected); - }); - } - - #[test] - fn test_edge_earliest_latest() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let mut res = graph.edge(1, 2).unwrap().earliest_time().unwrap(); - assert_eq!(res, 0); - - res = graph.edge(1, 2).unwrap().latest_time().unwrap(); - assert_eq!(res, 2); - - res = graph.at(1).edge(1, 2).unwrap().earliest_time().unwrap(); - assert_eq!(res, 1); - - res = graph.before(1).edge(1, 2).unwrap().earliest_time().unwrap(); - assert_eq!(res, 0); - - res = graph.after(1).edge(1, 2).unwrap().earliest_time().unwrap(); - assert_eq!(res, 2); - - res = graph.at(1).edge(1, 2).unwrap().latest_time().unwrap(); - assert_eq!(res, 1); - - res = graph.before(1).edge(1, 2).unwrap().latest_time().unwrap(); - assert_eq!(res, 0); - - res = graph.after(1).edge(1, 2).unwrap().latest_time().unwrap(); - assert_eq!(res, 2); - - let res_list: Vec = graph - .node(1) - .unwrap() - .edges() - .earliest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![0, 0]); - - let res_list: Vec = graph - .node(1) - .unwrap() - .edges() - .latest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![2, 2]); - - let res_list: Vec = graph - .node(1) - .unwrap() - .at(1) - .edges() - .earliest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![1, 1]); - - let res_list: Vec = graph - .node(1) - .unwrap() - .before(1) - .edges() - .earliest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![0, 0]); - - let res_list: Vec = graph - .node(1) - .unwrap() - .after(1) - .edges() - .earliest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![2, 2]); - - let res_list: Vec = graph - .node(1) - .unwrap() - .at(1) - .edges() - .latest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![1, 1]); - - let res_list: Vec = graph - .node(1) - .unwrap() - .before(1) - .edges() - .latest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![0, 0]); - - let res_list: Vec = graph - .node(1) - .unwrap() - .after(1) - .edges() - .latest_time() - .flatten() - .collect(); - assert_eq!(res_list, vec![2, 2]); - }); - } - - #[test] - fn node_properties() -> Result<(), GraphError> { - let g = Graph::new(); - - g.add_node( - 0, - 1, - [("t", Prop::str("wallet")), ("cost", Prop::F64(99.5))], - Some("a"), - None, - )?; - - let n1 = g.node(1).unwrap(); - g.add_node(1, 2, [("t", Prop::str("person"))], None, None)?; - g.add_node( - 6, - 3, - [ - ("list_prop", vec![1.1, 2.2, 3.3].into_prop_list()), - ("cost_b", Prop::F64(76.0)), - ], - Some("b"), - None, - )?; - - g.add_node( - 7, - 4, - [ - ("str_prop", Prop::str("hello")), - ("bool_prop", Prop::Bool(true)), - ], - Some("b"), - None, - )?; - - n1.add_metadata([("lol", Prop::str("smile"))])?; - - let node_1_props = n1 - .properties() - .iter() - .filter_map(|(k, v)| v.map(move |v| (k.to_string(), v))) - .collect::>(); - assert_eq!( - node_1_props, - vec![ - ("t".to_string(), Prop::str("wallet")), - ("cost".to_string(), Prop::F64(99.5)), - ] - ); - - assert_eq!(n1.metadata().as_vec(), [("lol".into(), "smile".into())]); - - Ok(()) - } - - #[test] - fn test_decimal_properties() { - let graph = Graph::new(); - let dec_prop = Prop::Decimal(BigDecimal::new(123456234234123123i64.into(), 9)); - graph - .add_node(0, 1, [("cost".to_string(), dec_prop.clone())], None, None) - .unwrap(); - - test_storage!(&graph, |graph| { - let node = graph.node(1).unwrap(); - let prop = node.properties().get("cost").unwrap(); - assert_eq!(prop, dec_prop); - }); - } - - #[test] - fn node_history_rows() { - let graph = Graph::new(); - graph - .add_node(1, 2, [("cool".to_string(), Prop::U64(1))], None, None) - .unwrap(); - graph - .add_node(0, 1, [("cool".to_string(), 1u64)], None, None) - .unwrap(); - graph - .add_node( - 1, - 1, - [ - ("coolio".to_string(), Prop::Bool(true)), - ("bla".to_string(), Prop::I64(2)), - ], - None, - None, - ) - .unwrap(); - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - graph - .add_node(1, 1, [("cool".to_string(), 3u64)], None, None) - .unwrap(); - - let node = graph.node(1).unwrap(); - - let actual = node - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); - - let expected = vec![ - (EventTime::new(0, 1), vec![Prop::U64(1)]), - (EventTime::new(1, 2), vec![Prop::Bool(true), Prop::I64(2)]), - (EventTime::new(1, 4), vec![Prop::U64(3)]), - (EventTime::new(2, 3), vec![]), - ]; - - assert_eq!(actual, expected); - - let node = graph.node(2).unwrap(); - - let actual = node - .rows() - .map(|(t, _, row)| (t, row.into_iter().map(|(_, a)| a).collect::>())) - .collect::>(); - - let expected = vec![(EventTime::new(1, 0), vec![Prop::U64(1)])]; - - assert_eq!(actual, expected); - } - - #[test] - fn check_node_history_str() { - let graph = Graph::new(); - - graph - .add_node(4, "Lord Farquaad", NO_PROPS, None, None) - .unwrap(); - graph - .add_node(6, "Lord Farquaad", NO_PROPS, None, None) - .unwrap(); - graph - .add_node(7, "Lord Farquaad", NO_PROPS, None, None) - .unwrap(); - graph - .add_node(8, "Lord Farquaad", NO_PROPS, None, None) - .unwrap(); - - let times_of_farquaad = graph.node("Lord Farquaad").unwrap().history(); - - assert_eq!(times_of_farquaad, [4, 6, 7, 8]); - - let view = graph.window(1, 8); - - let windowed_times_of_farquaad = view.node("Lord Farquaad").unwrap().history(); - assert_eq!(windowed_times_of_farquaad, [4, 6, 7]); - } - - #[test] - fn check_node_history_num() { - let graph = Graph::new(); - - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(3, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(4, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(8, 1, NO_PROPS, None, None).unwrap(); - - let times_of_one = graph.node(1).unwrap().history(); - - assert_eq!(times_of_one, [1, 2, 3, 4, 8]); - - let view = graph.window(1, 8); - - let windowed_times_of_one = view.node(1).unwrap().history(); - assert_eq!(windowed_times_of_one, [1, 2, 3, 4]); - } - - #[test] - fn check_edge_history() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(4, 1, 4, NO_PROPS, None).unwrap(); - let times_of_onetwo = graph.edge(1, 2).unwrap().history(); - let times_of_four = graph.edge(1, 4).unwrap().window(1, 5).history(); - let view = graph.window(2, 5); - let windowed_times_of_four = view.edge(1, 4).unwrap().window(2, 4).history(); - - assert_eq!(times_of_onetwo, [1, 3]); - assert_eq!(times_of_four, [4]); - assert!(windowed_times_of_four.is_empty()); - assert_eq!(graph.node(1).unwrap().edge_history_count(), 4); - } - - #[test] - fn check_node_edge_history_count() { - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(3, 0, 1, NO_PROPS, None).unwrap(); - - let node = graph.node(0).unwrap(); - assert_eq!(node.edge_history_count(), 2); - assert_eq!(node.after(1).edge_history_count(), 1); - assert_eq!(node.after(3).edge_history_count(), 0); - } - - use raphtory_storage::graph::nodes::node_storage_ops::NodeStorageOps; - - #[test] - fn check_edge_history_on_multiple_shards() { - let graph = Graph::new(); - - graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(4, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(5, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(6, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(7, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(8, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(9, 1, 4, NO_PROPS, None).unwrap(); - graph.add_edge(10, 1, 4, NO_PROPS, None).unwrap(); - - let times_of_onetwo = graph.edge(1, 2).unwrap().history(); - let times_of_four = graph.edge(1, 4).unwrap().window(1, 5).history(); - let times_of_outside_window = graph.edge(1, 4).unwrap().window(1, 4).history(); - let times_of_four_higher = graph.edge(1, 4).unwrap().window(6, 11).history(); - - let view = graph.window(1, 11); - let windowed_times_of_four = view.edge(1, 4).unwrap().window(2, 5).history(); - let windowed_times_of_four_higher = view.edge(1, 4).unwrap().window(8, 11).history(); - - assert_eq!(times_of_onetwo, [1, 3]); - assert_eq!(times_of_four, [4]); - assert_eq!(times_of_four_higher, [6, 7, 8, 9, 10]); - assert!(times_of_outside_window.is_empty()); - assert_eq!(windowed_times_of_four, [4]); - assert_eq!(windowed_times_of_four_higher, [8, 9, 10]); - } - - #[derive(Debug)] - struct CustomTime<'a>(&'a str, &'a str); - - impl TryIntoTimeNeedsEventId for CustomTime<'_> {} - - impl<'a> TryIntoTime for CustomTime<'a> { - fn try_into_time(self) -> Result { - let CustomTime(time, fmt) = self; - let time = NaiveDateTime::parse_from_str(time, fmt)?; - let time = time.and_utc().timestamp_millis(); - Ok(EventTime::from(time)) - } - } - - #[test] - fn test_ingesting_timestamps() { - let earliest_time = "2022-06-06 12:34:00".try_into_time().unwrap().t(); - let latest_time = "2022-06-07 12:34:00".try_into_time().unwrap().t(); - - let g = Graph::new(); - g.add_node("2022-06-06T12:34:00.000", 0, NO_PROPS, None, None) - .unwrap(); - g.add_edge("2022-06-07T12:34:00", 1, 2, NO_PROPS, None) - .unwrap(); - assert_eq!(g.earliest_time().unwrap().t(), earliest_time); - assert_eq!(g.latest_time().unwrap().t(), latest_time); - - let g = Graph::new(); - let fmt = "%Y-%m-%d %H:%M"; - - g.add_node(CustomTime("2022-06-06 12:34", fmt), 0, NO_PROPS, None, None) - .unwrap(); - g.add_edge(CustomTime("2022-06-07 12:34", fmt), 1, 2, NO_PROPS, None) - .unwrap(); - assert_eq!(g.earliest_time().unwrap(), earliest_time); - assert_eq!(g.latest_time().unwrap(), latest_time); - } - - #[test] - fn test_prop_display_str() { - let mut prop = Prop::Str("hello".into()); - assert_eq!(format!("{}", prop), "hello"); - - prop = Prop::I32(42); - assert_eq!(format!("{}", prop), "42"); - - prop = Prop::I64(9223372036854775807); - assert_eq!(format!("{}", prop), "9223372036854775807"); - - prop = Prop::U32(4294967295); - assert_eq!(format!("{}", prop), "4294967295"); - - prop = Prop::U64(18446744073709551615); - assert_eq!(format!("{}", prop), "18446744073709551615"); - - prop = Prop::U8(255); - assert_eq!(format!("{}", prop), "255"); - - prop = Prop::U16(65535); - assert_eq!(format!("{}", prop), "65535"); - - prop = Prop::F32(3.15159); - assert_eq!(format!("{}", prop), "3.15159"); - - prop = Prop::F64(3.151592653589793); - assert_eq!(format!("{}", prop), "3.151592653589793"); - - prop = Prop::Bool(true); - assert_eq!(format!("{}", prop), "true"); - } - - #[test] - fn test_graph_metadata_proptest() { - proptest!(|(u64_props: HashMap)| { - let g = Graph::new(); - - let as_props = u64_props - .into_iter() - .map(|(name, value)| (name, Prop::U64(value))) - .collect::>(); - - g.add_metadata(as_props.clone()).unwrap(); - - let props_map = as_props.into_iter().collect::>(); - - prop_assert!(props_map - .into_iter() - .all(|(name, value)| g.metadata().get(&name).unwrap() == value)); - }); - } - - #[test] - fn test_graph_metadata() { - let g = Graph::new(); - - let as_props: Vec<(&str, Prop)> = - vec![("mylist", Prop::list(vec![Prop::I64(1), Prop::I64(2)]))]; - - g.add_metadata(as_props.clone()).unwrap(); - - let props_names = as_props - .into_iter() - .map(|(name, _)| name.into()) - .collect::>(); - - assert_eq!(g.metadata().keys().collect::>(), props_names); - - let data = vec![ - ("key1", Prop::I64(10)), - ("key2", Prop::I64(20)), - ("key3", Prop::I64(30)), - ]; - let as_props: Vec<(&str, Prop)> = vec![("mylist2", Prop::map(data))]; - - g.add_metadata(as_props.clone()).unwrap(); - - let props_names2: HashSet = as_props - .into_iter() - .map(|(name, _)| name.into()) - .collect::>(); - - assert_eq!( - g.metadata().keys().collect::>(), - props_names - .union(&props_names2) - .cloned() - .collect::>() - ); - } - - #[test] - fn test_add_graph_metadata_with_existing_key_throws_error() { - let g = Graph::new(); - g.add_metadata(vec![("style", Prop::str("red"))]).unwrap(); - - assert!(g.add_metadata(vec![("style", Prop::str("blue"))]).is_err()); - assert_eq!(g.metadata().get("style").unwrap(), Prop::str("red")); // Value is unchanged - } - - #[test] - fn test_graph_metadata_with_maps() { - let g = Graph::new(); - - let style_with_size = Prop::map(vec![("fill", Prop::str("red")), ("size", Prop::I64(5))]); - - let style_with_opacity = Prop::map(vec![ - ("fill", Prop::str("red")), - ("opacity", Prop::F64(0.4)), - ]); - - // Add first metadata and verify - g.add_metadata(vec![("style", style_with_size.clone())]) - .unwrap(); - let actual = g.metadata().get("style").unwrap(); - assert_eq!(actual, style_with_size.clone()); - - // Update metadata and verify - g.update_metadata(vec![("style", style_with_opacity.clone())]) - .unwrap(); - let actual = g.metadata().get("style").unwrap(); - assert_eq!(actual, style_with_opacity.clone()); - - // Add another metadata property and verify - let config = Prop::map(vec![ - ("theme", Prop::str("dark")), - ("version", Prop::I64(2)), - ]); - g.add_metadata(vec![("config", config.clone())]).unwrap(); - let actual_config = g.metadata().get("config").unwrap(); - assert_eq!(actual_config, config.clone()); - - // Verify style is still the updated value - let actual_style = g.metadata().get("style").unwrap(); - assert_eq!(actual_style, style_with_opacity.clone()); - - // Verify all metadata keys exist - let keys: Vec<_> = g.metadata().keys().sorted().collect(); - assert_eq!(keys, vec!["config", "style"]); - } - - #[test] - fn test_graph_metadata_names() { - proptest!(|(u64_props: HashMap)| { - let g = Graph::new(); - - let as_props = u64_props - .into_iter() - .map(|(name, value)| (name.into(), Prop::U64(value))) - .collect::>(); - - g.add_metadata(as_props.clone()).unwrap(); - - let props_names = as_props - .into_iter() - .map(|(name, _)| name) - .collect::>(); - - prop_assert_eq!(g.metadata().keys().collect::>(), props_names); - }); - } - - #[test] - fn test_graph_temporal_props() { - proptest!(|(str_props: HashMap)| { - global_info_logger(); - - let g = Graph::new(); - let (t0, t1) = (1, 2); - - // Split properties into two sets based on even/odd index - // Even-indexed properties go to t0, odd-indexed to t1 - let mut t0_props = HashMap::new(); - let mut t1_props = HashMap::new(); - - for (i, (name, value)) in str_props.iter().enumerate() { - let prop_name: ArcStr = name.as_str().into(); - let prop_value = Prop::from(value.as_str()); - - if i % 2 == 0 { - t0_props.insert(prop_name, prop_value); - } else { - t1_props.insert(prop_name, prop_value); - } - } - - g.add_properties(t0, t0_props.clone()).unwrap(); - g.add_properties(t1, t1_props.clone()).unwrap(); - - // Verify properties can be retrieved at their timestamps - for (name, expected_value) in t0_props.iter() { - let actual = g.properties().temporal().get(name).unwrap().at(t0); - - prop_assert_eq!( - actual, - Some(expected_value.clone()), - "Property '{}' at t0 has wrong value", - name - ); - } - - for (name, expected_value) in t1_props.iter() { - let actual_value = g.properties().temporal().get(name).unwrap().at(t1); - - prop_assert_eq!( - actual_value, - Some(expected_value.clone()), - "Property '{}' at t1 has wrong value", - name - ); - } - - // Verify iter_latest returns all t0 properties - let actual_t0_props: HashMap<_, _> = g - .at(t0) - .properties() - .temporal() - .iter_latest() - .map(|(prop_name, prop_value)| (prop_name.clone(), prop_value)) - .collect(); - - prop_assert_eq!( - actual_t0_props, - t0_props, - "iter_latest() at t0 returned wrong properties" - ); - - // Verify latest returns correct values for t1 properties - for (name, expected_value) in t1_props.iter() { - let actual = g - .at(t1) - .properties() - .temporal() - .get(name) - .and_then(|v| v.latest()); - - prop_assert_eq!( - actual, - Some(expected_value.clone()), - "Property '{}' latest() at t1 has wrong value", - name - ); - } - }); - } - - #[test] - fn test_graph_temporal_props_with_maps() { - let g = Graph::new(); - - let style_with_size = Prop::map(vec![("fill", Prop::str("red")), ("size", Prop::I64(5))]); - - let style_with_opacity = Prop::map(vec![ - ("fill", Prop::str("red")), - ("opacity", Prop::F64(0.4)), - ]); - - // Add temporal properties with nested maps at different timestamps - g.add_properties(0, vec![("style", style_with_size.clone())]) - .unwrap(); - g.add_properties(1, vec![("style", style_with_opacity.clone())]) - .unwrap(); - g.add_properties(2, vec![("style", style_with_size.clone())]) - .unwrap(); - g.add_properties(3, vec![("style", style_with_opacity.clone())]) - .unwrap(); - - // Verify properties can be retrieved at their timestamps - let actual_t0 = g.properties().temporal().get("style").unwrap().at(0); - assert_eq!(actual_t0, Some(style_with_size.clone())); - - let actual_t1 = g.properties().temporal().get("style").unwrap().at(1); - assert_eq!(actual_t1, Some(style_with_opacity.clone())); - - let actual_t2 = g.properties().temporal().get("style").unwrap().at(2); - assert_eq!(actual_t2, Some(style_with_size.clone())); - - let actual_t3 = g.properties().temporal().get("style").unwrap().at(3); - assert_eq!(actual_t3, Some(style_with_opacity.clone())); - - // Verify history returns all timestamps - let history: Vec<_> = g - .properties() - .temporal() - .get("style") - .unwrap() - .history() - .collect(); - - assert_eq!(history, vec![0, 1, 2, 3]); - } - - #[test] - fn test_temporal_edge_props_window() { - let graph = Graph::new(); - graph - .add_edge(1, 1, 2, vec![("weight".to_string(), Prop::I64(1))], None) - .unwrap(); - graph - .add_edge(2, 1, 2, vec![("weight".to_string(), Prop::I64(2))], None) - .unwrap(); - graph - .add_edge(3, 1, 2, vec![("weight".to_string(), Prop::I64(3))], None) - .unwrap(); - test_storage!(&graph, |graph| { - let e = graph - .node(1) - .unwrap() - .out_edges() - .into_iter() - .next() - .unwrap(); - let res: HashMap> = e - .window(1, 3) - .properties() - .temporal() - .iter() - .map(|(k, v)| (k.clone(), v.iter().map(|(x, y)| (x.t(), y)).collect())) - .collect(); - - let mut exp = HashMap::new(); - exp.insert( - ArcStr::from("weight"), - vec![(1, Prop::I64(1)), (2, Prop::I64(2))], - ); - assert_eq!(res, exp); - }); - } - - #[test] - fn test_node_early_late_times() { - let graph = Graph::new(); - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(3, 1, NO_PROPS, None, None).unwrap(); - - // FIXME: Node add without properties not showing up (Issue #46) - assert_eq!(graph.node(1).unwrap().earliest_time().unwrap().t(), 1); - assert_eq!(graph.node(1).unwrap().latest_time().unwrap().t(), 3); - - assert_eq!(graph.at(2).node(1).unwrap().earliest_time().unwrap().t(), 2); - assert_eq!(graph.at(2).node(1).unwrap().latest_time().unwrap().t(), 2); - - assert_eq!( - graph - .before(2) - .node(1) - .unwrap() - .earliest_time() - .unwrap() - .t(), - 1 - ); - assert_eq!( - graph.before(2).node(1).unwrap().latest_time().unwrap().t(), - 1 - ); - - assert_eq!( - graph.after(2).node(1).unwrap().earliest_time().unwrap().t(), - 3 - ); - assert_eq!( - graph.after(2).node(1).unwrap().latest_time().unwrap().t(), - 3 - ); - } - - #[test] - fn test_node_ids() { - let graph = Graph::new(); - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 3, NO_PROPS, None, None).unwrap(); - - assert_eq!(graph.nodes().id().sort_by_id(), vec![1u64, 2u64, 3u64]); - - let g_at = graph.at(1); - assert_eq!(g_at.nodes().id().sort_by_id(), vec![1u64, 2u64]); - } - - #[test] - fn test_edge_layer_name() -> Result<(), GraphError> { - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None)?; - graph.add_edge(0, 0, 1, NO_PROPS, Some("awesome name"))?; - - test_storage!(&graph, |graph| { - let what = graph.edges().id().collect_vec(); - assert_eq!(what, vec![(0u64.into(), 1u64.into())]); - - let layer_names = graph.edges().layer_names().flatten().sorted().collect_vec(); - assert_eq!(layer_names, vec!["_default", "awesome name"]); - }); - Ok(()) - } - - #[test] - fn test_edge_from_single_layer() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer")).unwrap(); - - test_storage!(&graph, |graph| { - assert!(graph.edge(1, 2).is_some()); - assert!(graph.layers("layer").unwrap().edge(1, 2).is_some()) - }); - } - - #[test] - fn test_edge_layer_intersect_layer() { - let graph = Graph::new(); - - graph - .add_edge(1, 1, 2, NO_PROPS, Some("layer1")) - .expect("add edge"); - graph - .add_edge(1, 1, 3, NO_PROPS, Some("layer3")) - .expect("add edge"); - graph.add_edge(1, 1, 4, NO_PROPS, None).expect("add edge"); - - test_storage!(&graph, |graph| { - let g_layers = graph.layers(vec!["layer1", "layer3"]).expect("layer"); - - assert!(g_layers.layers("layer1").unwrap().edge(1, 2).is_some()); - assert!(g_layers.layers("layer3").unwrap().edge(1, 3).is_some()); - assert!(g_layers.edge(1, 2).is_some()); - assert!(g_layers.edge(1, 3).is_some()); - - assert!(g_layers.edge(1, 4).is_none()); - - let one = g_layers.node(1).expect("node"); - let ns = one - .neighbours() - .iter() - .filter_map(|v| v.id().as_u64()) - .collect::>(); - assert_eq!(ns, vec![2, 3]); - - let g_layers2 = g_layers.layers(vec!["layer1"]).expect("layer"); - - assert!(g_layers2.layers("layer1").unwrap().edge(1, 2).is_some()); - assert!(g_layers2.edge(1, 2).is_some()); - - assert!(g_layers2.edge(1, 3).is_none()); - - assert!(g_layers2.edge(1, 4).is_none()); - - let one = g_layers2.node(1).expect("node"); - let ns = one - .neighbours() - .iter() - .filter_map(|v| v.id().as_u64()) - .collect::>(); - assert_eq!(ns, vec![2]); - }); - } - - #[test] - fn simple_triangle() { - let graph = Graph::new(); - - let vs = vec![(1, 1, 2), (2, 1, 3), (3, 2, 1), (4, 3, 2)]; - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let windowed_graph = graph.window(0, 5); - let one = windowed_graph.node(1).expect("node"); - let ns_win = one - .neighbours() - .id() - .filter_map(|id| id.to_u64()) - .collect::>(); - - let one = graph.node(1).expect("node"); - let ns = one - .neighbours() - .id() - .filter_map(|id| id.to_u64()) - .collect::>(); - assert_eq!(ns, vec![2, 3]); - assert_eq!(ns_win, ns); - }); - } - - #[test] - fn test_layer_explode() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let e = graph.edge(1, 2).expect("edge"); - - let layer_exploded = e - .explode_layers() - .iter() - .filter_map(|e| { - e.edge.layer().and_then(|layer| { - Some((e.src().id().as_u64()?, e.dst().id().as_u64()?, layer)) - }) - }) - .collect::>(); - - assert_eq!( - layer_exploded, - vec![ - (1u64, 2u64, LayerId(1)), - (1u64, 2u64, LayerId(2)), - (1u64, 2u64, LayerId(3)), - ] - ); - }); - } - - #[test] - fn test_layer_explode_window() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let g = graph.window(0, 3); - let e = g.edge(1, 2).expect("edge"); - - let layer_exploded = e - .explode_layers() - .iter() - .filter_map(|e| { - e.layer_name() - .ok() - .map(|layer| (e.src().id(), e.dst().id(), layer)) - }) - .collect::>(); - - assert_eq!( - layer_exploded, - vec![ - (GID::U64(1), GID::U64(2), ArcStr::from("layer1")), - (GID::U64(1), GID::U64(2), ArcStr::from("layer2")) - ] - ); - }); - } - - #[test] - fn test_layer_explode_stacking() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let e = graph.edge(1, 2).expect("edge"); - - let layer_exploded = e - .explode_layers() - .iter() - .flat_map(|e| { - e.explode().into_iter().filter_map(|e| { - e.layer_name() - .ok() - .zip(e.time().ok().map(|t| t.t())) - .map(|(layer, t)| (t, e.src().id(), e.dst().id(), layer)) - }) - }) - .collect::>(); - - assert_eq!( - layer_exploded, - vec![ - (0, 1, 2, "layer1"), - (2, 1, 2, "layer1"), - (1, 1, 2, "layer2"), - (3, 1, 2, "_default"), - ] - .into_iter() - .map(|(a, b, c, d)| (a, GID::U64(b), GID::U64(c), ArcStr::from(d))) - .collect::>() - ); - }); - } - - #[test] - fn test_layer_explode_stacking_window() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let g = graph.window(0, 3); - let e = g.edge(1, 2).expect("edge"); - - let layer_exploded = e - .explode_layers() - .iter() - .flat_map(|e| { - e.explode().into_iter().filter_map(|e| { - e.layer_name() - .ok() - .zip(e.time().ok().map(|t| t.t())) - .map(|(layer, t)| (t, e.src().id(), e.dst().id(), layer)) - }) - }) - .collect::>(); - - assert_eq!( - layer_exploded, - vec![ - (0, 1, 2, "layer1"), - (2, 1, 2, "layer1"), - (1, 1, 2, "layer2") - ] - .into_iter() - .map(|(a, b, c, d)| { (a, GID::U64(b), GID::U64(c), ArcStr::from(d)) }) - .collect::>() - ); - }); - } - - #[test] - fn test_multi_layer_degree() { - let graph = Graph::new(); - graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 3, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); - - graph.add_edge(1, 1, 4, NO_PROPS, None).expect("failed"); - graph - .add_edge(1, 1, 2, NO_PROPS, "eth".into()) - .expect("failed"); - graph - .add_edge(1, 1, 3, NO_PROPS, "eth".into()) - .expect("failed"); - - graph - .add_edge(1, 2, 3, NO_PROPS, "eth".into()) - .expect("failed"); - graph.add_edge(1, 4, 3, NO_PROPS, None).expect("failed"); - - test_storage!(&graph, |graph| { - let actual = graph.node(1u64).map(|n| n.out_degree()); - assert_eq!(actual, Some(3)); - - let actual = graph.node(3u64).map(|n| n.in_degree()); - assert_eq!(actual, Some(3)); - - let actual = graph.node(3u64).map(|n| n.degree()); - assert_eq!(actual, Some(3)); - }); - } - - #[test] - fn test_multiple_layers_fundamentals() { - let graph = Graph::new(); - - graph - .add_edge(1, 1, 2, [("tx_sent", 10u64)], "btc".into()) - .expect("failed"); - graph - .add_edge(1, 1, 2, [("tx_sent", 20u64)], "eth".into()) - .expect("failed"); - graph - .add_edge(1, 1, 2, [("tx_sent", 70u64)], "tether".into()) - .expect("failed"); - - test_storage!(&graph, |graph| { - let e = graph.edge(1, 2).expect("failed to get edge"); - let sum: u64 = e - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum(); - - assert_eq!(sum, 100); - - let lg = graph - .layers(vec!["eth", "btc"]) - .expect("failed to layer graph"); - - let e = lg.edge(1, 2).expect("failed to get edge"); - - let sum_eth_btc: u64 = e - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum(); - - assert_eq!(sum_eth_btc, 30); - - assert_eq!(lg.count_edges(), 1); - - let e = graph.edge(1, 2).expect("failed to get edge"); - - let e_btc = e.layers("btc").expect("failed to get btc layer"); - let e_eth = e.layers("eth").expect("failed to get eth layer"); - - let edge_btc_sum = e_btc - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum::(); - - let edge_eth_sum = e_eth - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum::(); - - assert!(edge_btc_sum < edge_eth_sum); - - let e_eth = e_eth - .layers(vec!["eth", "btc"]) - .expect("failed to get eth,btc layers"); - - let eth_sum = e_eth - .properties() - .temporal() - .get("tx_sent") - .unwrap() - .iter() - .filter_map(|(_, prop)| prop.into_u64()) - .sum::(); - - // layer does not have a way to reset yet! - assert_eq!(eth_sum, 20); - }); - } - - #[test] - fn test_unique_layers() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - - test_storage!(&graph, |graph| { - assert_eq!( - graph - .layers("layer2") - .unwrap() - .unique_layers() - .collect_vec(), - vec!["layer2"] - ) - }); - } - - #[test] - fn node_from_id_is_consistent() { - proptest!(|(nodes: Vec)| { - let g = Graph::new(); - for v in nodes.iter() { - g.add_node(0, *v, NO_PROPS, None, None).unwrap(); - } - prop_assert!(g.nodes() - .name() - .into_iter_values() - .map(|name| g.node(name)) - .all(|v| v.is_some())); - }); - } - - #[test] - fn large_id_is_consistent() { - global_info_logger(); - let g = Graph::new(); - g.add_node(0, 10000000000000000006, NO_PROPS, None, None) - .unwrap(); - info!("names: {:?}", g.nodes().name().collect_vec()); - assert!(g - .nodes() - .name() - .into_iter_values() - .map(|name| g.node(name)) - .all(|v| v.is_some())) - } - - #[test] - fn exploded_edge_times_is_consistent() { - let edges = proptest::collection::vec( - ( - 0u64..100, - 0u64..100, - proptest::collection::vec(-1000i64..1000i64, 1..40), - ), - 1..400, - ); - proptest!(|(edges in edges, offset in -1000i64..1000i64)| { - prop_assert!(check_exploded_edge_times_is_consistent(edges, offset)); - }); - } - - #[test] - fn exploded_edge_times_is_consistent_1() { - let edges = vec![(0, 0, vec![0, 1])]; - assert!(check_exploded_edge_times_is_consistent(edges, 0)); - } - - fn check_exploded_edge_times_is_consistent( - edges: Vec<(u64, u64, Vec)>, - offset: i64, - ) -> bool { - global_info_logger(); - let mut correct = true; - let mut check = |condition: bool, message: String| { - if !condition { - error!("Failed: {}", message); - } - correct = correct && condition; - }; - // checks that exploded edges are preserved with correct timestamps - let mut edges: Vec<(GID, GID, Vec)> = edges - .into_iter() - .filter_map(|(src, dst, mut ts)| { - ts.sort(); - ts.dedup(); - let ts: Vec<_> = ts.into_iter().filter(|&t| t < i64::MAX).collect(); - (!ts.is_empty()).then_some((GID::U64(src), GID::U64(dst), ts)) - }) - .collect(); - edges.sort(); - edges.dedup_by_key(|(src, dst, _)| src.as_u64().zip(dst.as_u64())); - - let g = Graph::new(); - for (src, dst, times) in edges.iter() { - for &t in times.iter() { - if t < i64::MAX { - g.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - } - } - - let mut actual_edges: Vec<(GID, GID, Vec)> = g - .edges() - .iter() - .map(|e| { - ( - e.src().id(), - e.dst().id(), - e.explode() - .iter() - .map(|ee| { - check( - ee.earliest_time() == ee.latest_time(), - format!("times mismatched for {:?}", ee), - ); // times are the same for exploded edge - let t = ee.earliest_time().unwrap().t(); - check( - ee.at(t).is_active(), - format!("exploded edge {:?} inactive at {}", ee, t), - ); - let t_test = t.saturating_add(offset); - if t_test != t && t_test < i64::MAX && t_test > i64::MIN { - check( - !ee.at(t_test).is_active(), - format!("exploded edge {:?} active at {}", ee, t_test), - ); - } - t - }) - .collect(), - ) - }) - .collect(); - - for e in actual_edges.iter_mut() { - e.2.sort(); - } - actual_edges.sort(); - check( - actual_edges == edges, - format!( - "actual edges didn't match input actual: {:?}, expected: {:?}", - actual_edges, edges - ), - ); - correct - } - - #[test] - fn can_apply_algorithm_on_filtered_graph() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, [("layer", 1)], Some("1")).unwrap(); - graph.add_edge(1, 1, 3, [("layer", 1)], Some("1")).unwrap(); - graph.add_edge(1, 2, 3, [("layer", 2)], Some("2")).unwrap(); - graph.add_edge(2, 3, 4, [("layer", 2)], Some("2")).unwrap(); - graph.add_edge(0, 1, 3, [("layer", 2)], Some("2")).unwrap(); - - test_storage!(&graph, |graph| { - let wl = graph.window(0, 3).layers(vec!["1", "2"]).unwrap(); - assert_eq!(weakly_connected_components(&wl).groups().len(), 1); - }); - } - - #[test] - fn test_node_state_merge() { - let graph = Graph::new(); - for i in 0..1_000 { - graph.add_edge(0, i, i + 1, NO_PROPS, None).unwrap(); - } - - let sg = graph.subgraph(1..200); - let degs = degree_centrality(&graph); - let pr = unweighted_page_rank(&sg, None, None, None, false, None); - - let m1 = pr.state.merge( - °s.state, - MergePriority::Left, - MergePriority::Left, - Some(HashMap::from([( - "degree_centrality".to_string(), - MergePriority::Right, - )])), - ); - assert_eq!(m1.values().num_rows(), sg.count_nodes()); - - let m2 = degs.state.merge( - &pr.state, - MergePriority::Left, - MergePriority::Left, - Some(HashMap::from([( - "pagerank_score".to_string(), - MergePriority::Right, - )])), - ); - assert_eq!(m2.values().num_rows(), graph.count_nodes()); - } - - #[test] - #[cfg(feature = "proto")] - fn save_load_serial() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - let dir = tempfile::tempdir().unwrap(); - let file_path = dir.path().join("abcd11"); - g.encode(&file_path).unwrap(); - let gg = Graph::decode(&file_path).unwrap(); - assert_graph_equal(&g, &gg); - } - - #[test] - fn test_node_type_changes() { - let g = Graph::new(); - g.add_node(0, "A", NO_PROPS, Some("typeA"), None).unwrap(); - g.add_node(1, "A", NO_PROPS, None, None).unwrap(); - let node_a = g.node("A").unwrap(); - assert_eq!(node_a.node_type().as_str(), Some("typeA")); - let result = g.add_node(2, "A", NO_PROPS, Some("typeB"), None); - assert!(result.is_err()); - } - - #[test] - fn test_layer_degree() { - let g = Graph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - g.add_edge(1, 1, 2, NO_PROPS, Some("layer2")).unwrap(); - g.add_edge(2, 1, 3, NO_PROPS, Some("layer1")).unwrap(); - g.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - - test_storage!(&g, |g| { - let n = g.node(1).unwrap(); - let n_layer = n.layers("layer1").unwrap(); - assert_eq!(n_layer.out_degree(), 2); - assert_eq!(n_layer.in_degree(), 0); - assert_eq!(n_layer.degree(), 2); - }); - } - - #[test] - fn test_layer_name() { - let graph = Graph::new(); - - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph - .add_edge(0, 0, 2, NO_PROPS, Some("awesome layer")) - .unwrap(); - - assert_eq!(graph.edge(0, 1).unwrap().layer_names(), ["_default"]); - assert_eq!(graph.edge(0, 2).unwrap().layer_names(), ["awesome layer"]); - } - - #[test] - fn test_type_filter() { - let g = PersistentGraph::new(); - - g.add_node(1, 1, NO_PROPS, Some("wallet"), None).unwrap(); - g.add_node(1, 2, NO_PROPS, Some("timer"), None).unwrap(); - g.add_node(1, 3, NO_PROPS, Some("timer"), None).unwrap(); - g.add_node(1, 4, NO_PROPS, Some("wallet"), None).unwrap(); - - assert_eq!( - g.nodes() - .type_filter(["wallet"]) - .name() - .into_iter_values() - .sorted() - .collect_vec(), - vec!["1", "4"] - ); - - let g = Graph::new(); - g.add_node(1, 1, NO_PROPS, Some("a"), None).unwrap(); - g.add_node(1, 2, NO_PROPS, Some("b"), None).unwrap(); - g.add_node(1, 3, NO_PROPS, Some("b"), None).unwrap(); - g.add_node(1, 4, NO_PROPS, Some("a"), None).unwrap(); - g.add_node(1, 5, NO_PROPS, Some("c"), None).unwrap(); - g.add_node(1, 6, NO_PROPS, Some("e"), None).unwrap(); - g.add_node(1, 7, NO_PROPS, None, None).unwrap(); - g.add_node(1, 8, NO_PROPS, None, None).unwrap(); - g.add_node(1, 9, NO_PROPS, None, None).unwrap(); - g.add_edge(2, 1, 2, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 3, 2, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 2, 4, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 4, 5, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 4, 5, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 5, 6, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 3, 6, NO_PROPS, Some("a")).unwrap(); - - assert_eq!( - g.nodes() - .type_filter(["a", "b", "c", "e"]) - .name() - .sort_by_values(false), - vec!["1", "2", "3", "4", "5", "6"] - ); - - assert_eq!( - g.nodes().type_filter(Vec::::new()).name(), - Vec::::new() - ); - - assert_eq!( - g.nodes().type_filter([""]).name().sort_by_values(false), - vec!["7", "8", "9"] - ); - - let w = g.window(1, 4); - assert_eq!( - w.nodes().type_filter(["a"]).degree().sort_by_id(), - vec![1, 2] - ); - assert_eq!( - w.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c", "b"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - - let l = g.layers(["a"]).unwrap(); - assert_eq!( - l.nodes().type_filter(["a"]).degree().sort_by_id(), - vec![1, 2] - ); - assert_eq!( - l.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c", "b"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - - let sg = g.subgraph([1, 2, 3, 4, 5, 6]); - assert_eq!( - sg.nodes().type_filter(["a"]).degree().sort_by_id(), - vec![1, 2] - ); - assert_eq!( - sg.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c", "b"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect_vec()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - - assert_eq!( - g.nodes().degree().sort_by_id(), - vec![1, 3, 2, 2, 2, 2, 0, 0, 0] - ); - assert_eq!( - g.nodes().type_filter(["a"]).degree().sort_by_id(), - vec![1, 2] - ); - assert_eq!( - g.nodes().type_filter(["d"]).degree().sort_by_id(), - Vec::::new() - ); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .par_iter() - .map(|v| (v, v.degree())) - .collect::>() - .into_iter() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v) - .collect_vec(), - vec![1, 2] - ); - assert_eq!( - g.nodes() - .type_filter(["d"]) - .par_iter() - .map(|v| v.degree()) - .collect::>(), - Vec::::new() - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .collect() - .into_iter() - .map(|n| n.name()) - .collect_vec() - .into_iter() - .sorted() - .collect_vec(), - vec!["1", "4"] - ); - assert_eq!( - g.nodes() - .type_filter(Vec::<&str>::new()) - .collect() - .into_iter() - .map(|n| n.name()) - .collect_vec(), - Vec::<&str>::new() - ); - - assert_eq!(g.nodes().len(), 9); - assert_eq!(g.nodes().type_filter(["b"]).len(), 2); - assert_eq!(g.nodes().type_filter(["d"]).len(), 0); - - assert!(!g.nodes().is_empty()); - assert!(g.nodes().type_filter(["d"]).is_empty()); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .name() - .into_iter_values() - .collect_vec(), - vec!["1", "4"] - ); - assert_eq!( - g.nodes() - .type_filter(["a", "c"]) - .name() - .into_iter_values() - .sorted() - .collect_vec(), - vec!["1", "4", "5"] - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - assert_eq!( - g.nodes() - .type_filter(["a", "c"]) - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"], vec!["4", "6"]] - ); - assert_eq!( - g.nodes() - .type_filter(["d"]) - .neighbours() - .name() - .map(|(_, n)| n.collect_vec()) - .collect_vec(), - Vec::>::new() - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.collect::>()) - .collect_vec(), - vec![vec![], vec!["5"]] - ); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(Vec::<&str>::new()) - .name() - .map(|(_, n)| { n.collect::>() }) - .collect_vec(), - vec![vec![], Vec::<&str>::new()] - ); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c", "b"]) - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["2"], vec!["2", "5"]] - ); - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["d"]) - .name() - .map(|(_, n)| { n.collect::>() }) - .collect_vec(), - vec![vec![], Vec::<&str>::new()] - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec!["1", "3", "4"], vec!["1", "3", "4", "4", "6"]] - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["c"]) - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![vec![], vec!["4", "6"]] - ); - - assert_eq!( - g.nodes() - .neighbours() - .neighbours() - .name() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, v)| v.sorted().collect::>()) - .collect_vec(), - vec![ - vec!["1", "3", "4"], - vec!["2", "2", "2", "5", "6"], - vec!["1", "3", "3", "4", "5"], - vec!["1", "3", "4", "4", "6"], - vec!["2", "3", "5", "5"], - vec!["2", "4", "6", "6"], - vec![], - vec![], - vec![], - ] - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["d"]) - .total_count(), - 0 - ); - - assert!(g - .nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["d"]) - .is_all_empty()); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["d"]) - .iter() - .map(|(_, n)| { n.name().collect::>() }) - .collect_vec(), - vec![vec![], Vec::<&str>::new()] - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["b"]) - .collect() - .into_iter() - .flatten() - .map(|n| n.name()) - .collect_vec(), - vec!["2", "2"] - ); - - assert_eq!( - g.nodes() - .type_filter(["a"]) - .neighbours() - .type_filter(["d"]) - .collect() - .into_iter() - .flatten() - .map(|n| n.name()) - .collect_vec(), - Vec::<&str>::new() - ); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .name() - .sorted() - .collect_vec(), - vec!["1", "3", "4"] - ); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .type_filter(["b"]) - .name() - .collect_vec(), - vec!["3"] - ); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .type_filter(["d"]) - .name() - .collect_vec(), - Vec::<&str>::new() - ); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .type_filter(["c", "a"]) - .name() - .sorted() - .collect_vec(), - vec!["1", "4"] - ); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .type_filter(["c"]) - .neighbours() - .name() - .collect_vec(), - Vec::<&str>::new() - ); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .neighbours() - .name() - .sorted() - .collect_vec(), - vec!["2", "2", "2", "5", "6"], - ); - - assert_eq!( - g.node("2").unwrap().neighbours().type_filter(["d"]).len(), - 0 - ); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .type_filter(["a"]) - .neighbours() - .len(), - 3 - ); - - assert!(g - .node("2") - .unwrap() - .neighbours() - .type_filter(["d"]) - .is_empty()); - - assert!(!g - .node("2") - .unwrap() - .neighbours() - .type_filter(["a"]) - .neighbours() - .is_empty()); - - assert!(g - .node("2") - .unwrap() - .neighbours() - .type_filter(["d"]) - .neighbours() - .is_empty()); - - assert!(g - .node("2") - .unwrap() - .neighbours() - .type_filter(["d"]) - .iter() - .collect_vec() - .is_empty(),); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .type_filter(["b"]) - .collect() - .into_iter() - .map(|n| n.name()) - .collect_vec(), - vec!["3"] - ); - - assert_eq!( - g.node("2") - .unwrap() - .neighbours() - .type_filter(["d"]) - .collect() - .into_iter() - .map(|n| n.name()) - .collect_vec(), - Vec::<&str>::new() - ); - } - - #[test] - fn test_persistent_graph() { - let g = Graph::new(); - g.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) - .unwrap(); - assert_eq!( - g.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - - let pg = g.persistent_graph(); - pg.delete_edge(10, 0, 1, None).unwrap(); - assert_eq!( - g.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - } - - #[test] - fn test_unique_property() { - let g = Graph::new(); - g.add_edge(1, 1, 2, [("status", "open")], None).unwrap(); - g.add_edge(2, 1, 2, [("status", "open")], None).unwrap(); - g.add_edge(3, 1, 2, [("status", "review")], None).unwrap(); - g.add_edge(4, 1, 2, [("status", "open")], None).unwrap(); - g.add_edge(5, 1, 2, [("status", "in-progress")], None) - .unwrap(); - g.add_edge(10, 1, 2, [("status", "in-progress")], None) - .unwrap(); - g.add_edge(9, 1, 2, [("state", true)], None).unwrap(); - g.add_edge(10, 1, 2, [("state", false)], None).unwrap(); - g.add_edge(6, 1, 2, NO_PROPS, None).unwrap(); - - let mut props = g - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("status") - .unwrap() - .unique() - .into_iter() - .map(|x| x.unwrap_str().to_string()) - .collect_vec(); - props.sort(); - assert_eq!(props, vec!["in-progress", "open", "review"]); - - let ordered_dedupe_latest = g - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("status") - .unwrap() - .ordered_dedupe(true) - .into_iter() - .map(|(x, y)| (x.t(), y.unwrap_str().to_string())) - .collect_vec(); - - assert_eq!( - ordered_dedupe_latest, - vec![ - (2, "open".to_string()), - (3, "review".to_string()), - (4, "open".to_string()), - (10, "in-progress".to_string()), - ] - ); - - let ordered_dedupe_earliest = g - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("status") - .unwrap() - .ordered_dedupe(false) - .into_iter() - .map(|(x, y)| (x.t(), y.unwrap_str().to_string())) - .collect_vec(); - - assert_eq!( - ordered_dedupe_earliest, - vec![ - (1, "open".to_string()), - (3, "review".to_string()), - (4, "open".to_string()), - (5, "in-progress".to_string()), - ] - ); - } - - #[test] - fn test_create_node() { - let g = Graph::new(); - g.create_node(0, 1, [("test", Prop::Bool(true))], None, None) - .unwrap(); - - let n = g.node(1).unwrap(); - - assert_eq!(n.id().as_u64().unwrap(), 1); - assert_eq!(n.properties().get("test").unwrap(), Prop::Bool(true)); - - let result = g.create_node(1, 1, [("test".to_string(), Prop::Bool(true))], None, None); - assert!(matches!(result, Err(GraphError::NodeExistsError(id)) if id == GID::U64(1))); - } - - #[test] - fn test_materialize_edge_metadata() { - let g = Graph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.add_metadata([("test", "test")], None).unwrap(); - g.add_edge(10, 1, 2, NO_PROPS, Some("a")).unwrap(); - - let gw = g.after(1); - let gmw = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gmw); - } - - #[test] - fn test_id_filter() { - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - assert_eq!(graph.nodes().id(), [0, 1]); - assert_eq!(graph.nodes().id_filter([0]).len(), 1); - assert_eq!(graph.nodes().id_filter([0]).id(), [0]); - assert_eq!(graph.nodes().id_filter([0]).degree(), [1]); - } - - #[test] - fn test_indexed() { - proptest!(|(graph in build_graph_strat(10, 10, 10, 10, false), nodes in subsequence((0..10).collect::>(), 0..10))| { - let graph = Graph::from(build_graph(&graph)); - let expected_node_ids = nodes.iter().copied().filter(|&id| graph.has_node(id)).collect::>(); - let nodes = graph.nodes().id_filter(nodes); - assert_eq!(nodes.id(), expected_node_ids); - }) - } - - #[test] - fn materialize_window_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = Graph::from(build_graph(&graph_f)); - let gw = g.window(w.start, w.end); - let gmw = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gmw); - }) - } - - #[test] - fn materialize_temporal_properties_one_edge() { - let g = Graph::new(); - g.add_edge( - 0, - 0, - 0, - [("3", Prop::I64(1)), ("0", Prop::str("baa"))], - Some("a"), - ) - .unwrap(); - - let gw = g.window(-9, 3); - let gmw = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gmw); - } - - #[test] - fn materialize_one_node() { - let g = Graph::new(); - g.add_node(0, 0, NO_PROPS, None, None).unwrap(); - - let n = g.node(0).unwrap(); - let hist = n.history(); - assert!(!hist.is_empty()); - let rows = n.rows().collect::>(); - assert!(!rows.is_empty()); - - let gw = g.window(0, 1); - let gmw = gw.materialize().unwrap(); - - assert_graph_equal(&gw, &gmw); - } - - #[test] - fn materialize_some_edges() -> Result<(), GraphError> { - let edges1_props = EdgeUpdatesFixture { - props: PropUpdatesFixture { - t_props: vec![ - (2433054617899119663, vec![]), - ( - 5623371002478468619, - vec![("0".to_owned(), Prop::I64(-180204069376666762))], - ), - ], - c_props: vec![], - }, - deletions: vec![-3684372592923241629, 3668280323305195349], - }; - - let edges2_props = EdgeUpdatesFixture { - props: PropUpdatesFixture { - t_props: vec![ - ( - -7888823724540213280, - vec![("0".to_owned(), Prop::I64(1339447446033500001))], - ), - (-3792330935693192039, vec![]), - ( - 4049942931077033460, - vec![("0".to_owned(), Prop::I64(-544773539725842277))], - ), - (5085404190610173488, vec![]), - (1445770503123270290, vec![]), - (-5628624083683143619, vec![]), - (-394401628579820652, vec![]), - (-2398199704888544233, vec![]), - ], - c_props: vec![("0".to_owned(), Prop::I64(-1877019573933389749))], - }, - deletions: vec![ - 3969804007878301015, - 7040207277685112004, - 7380699292468575143, - 3332576590029503186, - -1107894292705275349, - 6647229517972286485, - 6359226207899406831, - ], - }; - - let edges: EdgeFixture = [ - ((2, 7, Some("b")), edges1_props), - ((7, 2, Some("a")), edges2_props), - ] - .into_iter() - .collect(); - - let w = -3619743214445905380..90323088878877991; - let graph_f = GraphFixture { - nodes: NodeFixture::default(), - edges, - }; - - let g = Graph::from(build_graph(&graph_f)); - let gw = g.window(w.start, w.end); - let gmw = gw.materialize()?; - assert_graph_equal(&gw, &gmw); - - Ok(()) - } - - #[test] - fn materialize_window_delete_test() { - let g = Graph::new(); - g.delete_edge(0, 0, 0, Some("a")).unwrap(); - let w = 0..1; - let gw = g.window(w.start, w.end); - let gmw = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gmw); - } - - #[test] - fn test_multilayer() { - let g = Graph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(1, 0, 0, NO_PROPS, Some("a")).unwrap(); - let gw = g.window(0, 1); - - let expected = Graph::new(); - expected.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - expected.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gw, &expected); - } - - #[test] - fn test_empty_window() { - let g = Graph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - let gw = g.window(-1, 0); - - assert!(g.window(-1, 0).nodes().is_empty()); - assert_eq!(g.window(-1, 0).count_nodes(), 0); - for layer in gw.unique_layers() { - let layered = gw.valid_layers(layer); - assert_eq!(layered.count_nodes(), 0); - } - } - - #[test] - fn add_edge_and_read_props_concurrent() { - for t in 0..1000 { - let g = Graph::new(); - join( - || g.add_edge(t, 1, 2, [("test", true)], None).unwrap(), - || { - // if the edge exists already, it should have the property set - g.window(t, t + 1) - .edge(1, 2) - .map(|e| assert!(e.properties().get("test").is_some())) - }, - ); - } - } - - #[test] - fn test_group_by() { - let g = Graph::new(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); - g.add_edge(0, 4, 5, NO_PROPS, None).unwrap(); - let groups_from_lazy = g.nodes().out_degree().groups(); - - let groups_from_eager = g.nodes().out_degree().compute().groups(); - - let expected_groups: HashMap> = HashMap::from([ - (0, Arc::from_iter([GID::U64(3), GID::U64(5)])), - (1, Arc::from_iter([GID::U64(1), GID::U64(2), GID::U64(4)])), - ]); - - let expected_subgraphs: HashMap> = HashMap::from([ - (0, Arc::from_iter([])), - (1, Arc::from_iter([GID::U64(1), GID::U64(2)])), - ]); - - assert_eq!( - groups_from_lazy - .iter() - .map(|(v, nodes)| (*v, nodes.id().sort_by_values(false).values().clone())) - .collect::>(), - expected_groups - ); - - assert_eq!( - groups_from_lazy - .clone() - .into_iter_groups() - .map(|(v, nodes)| (v, nodes.id().sort_by_values(false).values().clone())) - .collect::>(), - expected_groups - ); - - assert_eq!( - groups_from_lazy - .iter_subgraphs() - .map(|(v, graph)| ( - *v, - graph.nodes().id().sort_by_values(false).values().clone() - )) - .collect::>(), - expected_subgraphs - ); - - assert_eq!( - groups_from_lazy - .clone() - .into_iter_subgraphs() - .map(|(v, graph)| (v, graph.nodes().id().sort_by_values(false).values().clone())) - .collect::>(), - expected_subgraphs - ); - - assert_eq!( - groups_from_eager - .iter() - .map(|(v, nodes)| (*v, nodes.id().sort_by_values(false).values().clone())) - .collect::>(), - expected_groups - ); - - assert_eq!(groups_from_lazy.len(), expected_groups.len()); - - for (i, (v, nodes)) in groups_from_eager.iter().enumerate() { - let (v2, nodes2) = groups_from_eager.group(i).unwrap(); - assert_eq!(v, v2); - assert!(nodes.iter().eq(nodes2.iter())); - let (v3, graph) = groups_from_eager.group_subgraph(i).unwrap(); - assert_eq!(v, v3); - assert_eq!( - graph.nodes().id().sort_by_values(false), - expected_subgraphs[v].deref() - ); - } - } -} diff --git a/raphtory/tests/edge_property_filter.rs b/raphtory/tests/edge_property_filter.rs deleted file mode 100644 index e2497332f2..0000000000 --- a/raphtory/tests/edge_property_filter.rs +++ /dev/null @@ -1,384 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use proptest::{arbitrary::any, proptest}; - use raphtory::{ - db::{ - api::view::Filter, - graph::{ - assertions::{assert_ok_or_missing_edges, EdgeRow}, - graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, - views::{ - deletion_graph::PersistentGraph, - filter::model::{ - node_filter::ops::NodeFilterOps, property_filter::ops::PropertyFilterOps, - ComposableFilter, EdgeFilter, PropertyFilterFactory, - }, - }, - }, - }, - prelude::*, - test_utils::{ - build_edge_deletions, build_edge_list, build_graph_from_edge_list, build_window, - }, - }; - use raphtory_api::core::{entities::properties::prop::PropType, storage::timeindex::AsTime}; - use raphtory_storage::mutation::addition_ops::{InternalAdditionOps, SessionAdditionOps}; - - #[test] - fn test_edge_filter() { - let g = Graph::new(); - g.add_edge(0, "Jimi", "John", [("band", "JH Experience")], None) - .unwrap(); - g.add_edge(1, "John", "David", [("band", "Dead & Company")], None) - .unwrap(); - g.add_edge(2, "David", "Jimi", [("band", "Pink Floyd")], None) - .unwrap(); - - let filter_expr = EdgeFilter::dst() - .name() - .eq("David") - .and(EdgeFilter.property("band").eq("Dead & Company")); - let filtered_edges = g.filter(filter_expr).unwrap(); - - assert_eq!( - filtered_edges - .edges() - .iter() - .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) - .collect::>(), - vec!["John->David"] - ); - - let g_expected = Graph::new(); - g_expected - .add_edge((1, 1), "John", "David", [("band", "Dead & Company")], None) - .unwrap(); - - assert_graph_equal(&filtered_edges, &g_expected); - } - - #[test] - fn test_edge_filter_persistent() { - let g = PersistentGraph::new(); - g.add_edge(0, "Jimi", "John", [("band", "JH Experience")], None) - .unwrap(); - g.add_edge(1, "John", "David", [("band", "Dead & Company")], None) - .unwrap(); - g.add_edge(2, "David", "Jimi", [("band", "Pink Floyd")], None) - .unwrap(); - - let filter_expr = EdgeFilter::dst() - .name() - .eq("David") - .and(EdgeFilter.property("band").eq("Dead & Company")); - let filtered_edges = g.filter(filter_expr).unwrap(); - - let g_expected = PersistentGraph::new(); - g_expected - .add_edge((1, 1), "John", "David", [("band", "Dead & Company")], None) - .unwrap(); - - assert_eq!( - filtered_edges - .edges() - .iter() - .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) - .collect::>(), - vec!["John->David"] - ); - assert_graph_equal(&filtered_edges, &g_expected); - } - - #[test] - fn test_edge_property_filter_on_nodes() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("test", 1i64)], None).unwrap(); - g.add_edge(0, 1, 3, [("test", 3i64)], None).unwrap(); - g.add_edge(1, 2, 3, [("test", 2i64)], None).unwrap(); - g.add_edge(1, 2, 4, [("test", 0i64)], None).unwrap(); - - let filter = EdgeFilter.property("test").eq(1i64); - let n1 = g.node(1).unwrap().filter(filter).unwrap(); - assert_eq!( - n1.edges().id().collect_vec(), - vec![(GID::U64(1), GID::U64(2))] - ); - let n2 = g - .node(2) - .unwrap() - .filter(EdgeFilter.property("test").gt(1i64)) - .unwrap(); - assert_eq!( - n2.edges().id().collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - } - - #[test] - fn test_filter() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("test", 1i64)], None).unwrap(); - g.add_edge(1, 2, 3, [("test", 2i64)], None).unwrap(); - - let filter = EdgeFilter.property("test").eq(1i64); - let gf = g.filter(filter).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(1), GID::U64(2))] - ); - let gf = g.filter(EdgeFilter.property("test").gt(1i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - } - - #[test] - fn test_filter_gt() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() > v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); - } - } - }); - }) - } - - #[test] - fn test_filter_ge() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").ge(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() >= v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); - } - } - }); - }) - } - - #[test] - fn test_filter_lt() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").lt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() < v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); - } - } - }); - }) - } - - #[test] - fn test_filter_le() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").le(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() <= v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); - } - } - }); - }) - } - - #[test] - fn test_filter_eq() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() == v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); - } - } - }); - }) - } - - #[test] - fn test_filter_ne() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = EdgeFilter.property("int_prop").ne(v); - assert_ok_or_missing_edges(&edges, g.filter(filter), |filtered| { - for e in g.edges().iter() { - if e.properties().get("int_prop").unwrap_i64() != v { - assert!(filtered.has_edge(e.src(), e.dst())); - } else { - assert!(!filtered.has_edge(e.src(), e.dst())); - } - } - }); - }) - } - - #[test] - fn test_graph_materialise_window() { - proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { - let g = build_graph_from_edge_list(&edges); - for (src, dst, t) in edge_deletions { - g.delete_edge(t, src, dst, None).unwrap(); - } - let filter = EdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.window(start, end).filter(filter.clone()), |filtered| { - let gwfm = filtered.materialize().unwrap(); - assert_graph_equal(&filtered, &gwfm); - }); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let gwf = filtered.window(start, end); - let gwfm = gwf.materialize().unwrap(); - assert_graph_equal(&gwf, &gwfm); - }); - }) - } - - fn check_persistent_graph_mat_window( - edges: &[EdgeRow], - edge_deletions: Vec<(u64, u64, i64)>, - v: i64, - (start, end): (i64, i64), - ) { - let g = build_graph_from_edge_list(edges); - let g = g.persistent_graph(); - for (src, dst, t) in edge_deletions { - g.delete_edge(t, src, dst, None).unwrap(); - } - let filter = EdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges( - edges, - g.window(start, end).filter(filter.clone()), - |filtered| { - let gwfm = filtered.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&filtered, &gwfm); - }, - ); - assert_ok_or_missing_edges(edges, g.filter(filter.clone()), |filtered| { - let gfw = filtered.window(start, end); - let gfwm = gfw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gfw, &gfwm); - }); - } - - #[test] - fn test_persistent_graph_materialise_window() { - proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { - check_persistent_graph_mat_window(&edges, edge_deletions, v, (start, end)); - }) - } - - #[test] - fn simplte_graph_materialize_window() { - let edges = [(0, 0, 0, "".to_owned(), 0), (0, 0, 0, "".to_owned(), 0)]; - let edge_deletions = vec![]; - let start_end = (1, 2); - let v = -1; - check_persistent_graph_mat_window(&edges, edge_deletions, v, start_end); - } - - #[test] - fn test_single_unfiltered_edge_empty_window_persistent() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.delete_edge(10, 0, 1, None).unwrap(); - let gw = g - .filter(EdgeFilter.property("test").gt(0i64)) - .unwrap() - .window(0, 0); - - assert_eq!(gw.count_edges(), 0); - let expected = PersistentGraph::new(); - expected - .write_session() - .unwrap() - .resolve_edge_property("test", PropType::I64, false) - .unwrap(); - expected.resolve_layer(None).unwrap(); - assert_graph_equal(&gw, &expected) - } - - #[test] - fn test_single_deleted_edge_window_persistent() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.delete_edge(1, 0, 1, None).unwrap(); - let gw = g - .filter(EdgeFilter.property("test").gt(0i64)) - .unwrap() - .window(0, 2); - let gm = gw.materialize().unwrap(); - - assert_eq!(gw.count_edges(), 1); - assert_eq!(gw.count_temporal_edges(), 1); - - assert_eq!(gw.node(0).unwrap().edge_history_count(), 2); - assert_eq!(gw.node(0).unwrap().after(0).edge_history_count(), 1); - - assert_persistent_materialize_graph_equal(&gw, &gm) - } - - #[test] - fn test_single_unfiltered_edge_window_persistent_2() { - let g = PersistentGraph::new(); - g.add_edge(1, 0, 1, [("test", 1i64)], None).unwrap(); - g.delete_edge(0, 0, 0, None).unwrap(); - - let gwf = g - .window(-1, 2) - .filter(EdgeFilter.property("test").gt(0i64)) - .unwrap(); - assert!(gwf.has_edge(0, 1)); - assert!(!gwf.has_edge(0, 0)); - assert_eq!(gwf.node(0).unwrap().earliest_time().map(|t| t.t()), Some(1)); - assert_persistent_materialize_graph_equal(&gwf, &gwf.materialize().unwrap()); - - let gfw = g - .filter(EdgeFilter.property("test").gt(0i64)) - .unwrap() - .window(-1, 2); - let gm = gfw.materialize().unwrap(); - - assert_eq!(gfw.count_edges(), 1); - assert_eq!(gfw.count_temporal_edges(), 1); - - assert_eq!(gfw.node(0).unwrap().edge_history_count(), 1); - assert_eq!(gfw.node(0).unwrap().after(0).edge_history_count(), 1); - - assert_persistent_materialize_graph_equal(&gfw, &gm) - } -} diff --git a/raphtory/tests/exploded_edge_property_filter.rs b/raphtory/tests/exploded_edge_property_filter.rs deleted file mode 100644 index e5bc97d425..0000000000 --- a/raphtory/tests/exploded_edge_property_filter.rs +++ /dev/null @@ -1,678 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use proptest::{arbitrary::any, proptest}; - use raphtory::{ - db::{ - api::view::{Filter, StaticGraphViewOps}, - graph::{ - assertions::assert_ok_or_missing_edges, - edge::EdgeView, - graph::{ - assert_graph_equal, assert_node_equal, assert_nodes_equal, - assert_persistent_materialize_graph_equal, - }, - views::{ - deletion_graph::PersistentGraph, - filter::model::{ - property_filter::ops::PropertyFilterOps, ExplodedEdgeFilter, - PropertyFilterFactory, - }, - }, - }, - }, - prelude::*, - test_utils::{ - build_edge_deletions, build_edge_list, build_edge_list_with_deletions, - build_graph_from_edge_list, build_window, Update, - }, - }; - use raphtory_api::core::{ - entities::properties::prop::PropType, - storage::{arc_str::ArcStr, timeindex::AsTime}, - }; - use raphtory_core::entities::nodes::node_ref::AsNodeRef; - use raphtory_storage::{ - core_ops::CoreGraphOps, - mutation::addition_ops::{InternalAdditionOps, SessionAdditionOps}, - }; - use std::collections::HashMap; - - fn build_filtered_graph( - edges: &[(u64, u64, i64, String, i64)], - filter: impl Fn(i64) -> bool, - ) -> Graph { - let g = Graph::new(); - for (index, (src, dst, t, str_prop, int_prop)) in edges.iter().enumerate() { - if filter(*int_prop) { - g.add_edge( - (*t, index), - *src, - *dst, - [ - ("str_prop", Prop::str(str_prop.as_ref())), - ("int_prop", Prop::I64(*int_prop)), - ], - None, - ) - .unwrap(); - } - } - if !edges.is_empty() { - g.resolve_layer(None).unwrap(); - } - g - } - - fn build_filtered_nodes_graph( - edges: &[(u64, u64, i64, String, i64)], - filter: impl Fn(i64) -> bool, - ) -> Graph { - let g = Graph::new(); - for (src, dst, t, str_prop, int_prop) in edges { - if filter(*int_prop) { - g.add_edge( - *t, - *src, - *dst, - [ - ("str_prop", str_prop.as_str().into()), - ("int_prop", Prop::I64(*int_prop)), - ], - None, - ) - .unwrap(); - } - g.atomic_add_node(src.as_node_ref()).unwrap(); - g.atomic_add_node(dst.as_node_ref()).unwrap(); - } - if !edges.is_empty() { - g.resolve_layer(None).unwrap(); - } - g - } - - fn build_filtered_persistent_graph( - edges: HashMap<(u64, u64), Vec<(i64, Update)>>, - filter: impl Fn(i64) -> bool, - ) -> (PersistentGraph, PersistentGraph) { - let g = PersistentGraph::new(); - let g_filtered = PersistentGraph::new(); - if !edges.iter().all(|(_, v)| v.is_empty()) { - g_filtered.resolve_layer(None).unwrap(); - } - for ((src, dst), updates) in edges { - for (t, update) in updates { - match update { - Update::Deletion => { - g.delete_edge(t, src, dst, None).unwrap(); - g_filtered.delete_edge(t, src, dst, None).unwrap(); - } - Update::Addition(str_prop, int_prop) => { - g.add_edge( - t, - src, - dst, - [ - ("str_prop", str_prop.clone().into()), - ("int_prop", Prop::I64(int_prop)), - ], - None, - ) - .unwrap(); - if filter(int_prop) { - g_filtered - .add_edge( - t, - src, - dst, - [ - ("str_prop", str_prop.into()), - ("int_prop", Prop::I64(int_prop)), - ], - None, - ) - .unwrap(); - } else { - g_filtered.delete_edge(t, src, dst, None).unwrap(); - // properties still exist after filtering - let session = g_filtered.write_session().unwrap(); - session - .resolve_edge_property("str_prop", PropType::Str, false) - .unwrap(); - session - .resolve_edge_property("int_prop", PropType::I64, false) - .unwrap(); - } - } - } - } - } - (g, g_filtered) - } - - fn edge_attr( - e: &EdgeView, - ) -> (String, String, Option, Option) { - let src = e.src().name(); - let dst = e.dst().name(); - let int_prop = e.properties().get("int_prop"); - let str_prop = e.properties().get("str_prop"); - (src, dst, int_prop.into_i64(), str_prop.into_str()) - } - - #[test] - fn test_filter_gt() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv > v); - assert_graph_equal(&filtered, &expected_filtered_g); - #[cfg(feature = "search")] - { - let search_ee = g.search_exploded_edges(filter, 100, 0).unwrap(); - let filter_ee = filtered.edges().explode().collect(); - let from_search = search_ee.iter().map(edge_attr).collect_vec(); - let from_filter = filter_ee.iter().map(edge_attr).collect_vec(); - assert_eq!(from_search, from_filter); - } - }); - }) - } - - #[test] - fn test_filter_gt_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv > v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").gt(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) - } - - #[test] - fn test_one_edge() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("int_prop", 0i64)], None).unwrap(); - let filtered = g - .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) - .unwrap(); - let gf = Graph::new(); - gf.resolve_layer(None).unwrap(); - assert_eq!(filtered.count_nodes(), 0); - assert_eq!(filtered.count_edges(), 0); - assert_graph_equal(&filtered, &gf); - } - - #[test] - fn test_filter_ge() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").ge(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv >= v); - assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) - } - - #[test] - fn test_filter_ge_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv >= v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - if p.is_some() { - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").ge(v) - ).unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } - - }) - } - - #[test] - fn test_filter_persistent_single_filtered_edge() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, [("test", 1i64)], None).unwrap(); - let gf = g - .filter(ExplodedEdgeFilter.property("test").gt(1i64)) - .unwrap(); - - assert_eq!(gf.count_edges(), 1); - assert_eq!(gf.count_temporal_edges(), 0); - - let expected = PersistentGraph::new(); - expected.delete_edge(0, 0, 0, None).unwrap(); - //the property still exists! - expected - .write_session() - .unwrap() - .resolve_edge_property("test", PropType::I64, false) - .unwrap(); - - assert_graph_equal(&gf, &expected); - } - - #[test] - fn test_filter_lt() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").lt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv < v); - assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) - } - - #[test] - fn test_filter_lt_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv < v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").lt(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) - } - - #[test] - fn test_filter_le() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").le(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv <= v); - assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) - } - - #[test] - fn test_filter_le_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv <= v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").le(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) - } - - #[test] - fn test_filter_eq() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); - assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) - } - - #[test] - fn test_filter_eq_one_edge() { - let g = Graph::new(); - g.add_edge(0, 0, 0, [("int_prop", Prop::I64(0))], None) - .unwrap(); - let filter = ExplodedEdgeFilter.property("int_prop").eq(0i64); - assert_graph_equal(&g.filter(filter.clone()).unwrap(), &g); - } - - #[test] - fn test_filter_eq_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv == v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").eq(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) - } - - #[test] - fn test_filter_ne() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").ne(v); - assert_ok_or_missing_edges(&edges, g.filter(filter), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv != v); - assert_graph_equal(&filtered, &expected_filtered_g); - }); - }) - } - - #[test] - fn test_filter_ne_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv != v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").ne(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered, &expected_filtered_g); - } else { - assert!(filtered.is_err()) - } - }) - } - - #[test] - fn test_filter_window() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::(), (start, end) in build_window() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); - assert_graph_equal(&filtered.window(start, end), &expected_filtered_g.window(start, end)); - }); - }); - } - - #[test] - fn test_filter_window_persistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::(), (start, end) in build_window() - )| { - let (g, expected_filtered_g) = build_filtered_persistent_graph(edges, |vv| vv >= v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").ge(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - assert_graph_equal(&filtered.window(start, end), &expected_filtered_g.window(start, end)); - } else { - assert!(filtered.is_err()) - } - }) - } - - #[test] - fn test_filter_materialise_is_consistent() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let mat = filtered.materialize().unwrap(); - assert_graph_equal(&filtered, &mat); - }); - }) - } - - #[test] - fn test_filter_persistent_materialise_is_consistent() { - proptest!(|( - edges in build_edge_list_with_deletions(100, 100), v in any::() - )| { - let (g, expected) = build_filtered_persistent_graph(edges, |vv| vv >= v); - let p = g.edge_meta().temporal_prop_mapper().get_id("int_prop"); - let filtered = g.filter( - ExplodedEdgeFilter.property("int_prop").ge(v) - ); - if p.is_some() { - let filtered = filtered.unwrap(); - let mat = filtered.materialize().unwrap(); - assert_graph_equal(&filtered, &mat); - assert_graph_equal(&expected, &mat); - } else { - assert!(filtered.is_err()) - } - }) - } - - #[test] - #[ignore = "need a way to add a node without timestamp"] - fn test_filter_on_nodes() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.nodes().filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_nodes_graph(&edges, |vv| vv == v); - assert_nodes_equal(&filtered, &expected_filtered_g.nodes()); - }); - }) - } - - #[test] - #[ignore = "need a way to add a node without timestamp"] - fn test_filter_on_nodes_simple() { - let edges = [(1u64, 2u64, 0i64, "a".to_string(), 10i64)]; - let v = -1; - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.nodes().filter(filter.clone()), |filtered| { - let expected_filtered_g = build_filtered_nodes_graph(&edges, |vv| vv == v); - assert_nodes_equal(&filtered, &expected_filtered_g.nodes()); - }); - } - - #[test] - fn test_filter_on_node() { - proptest!(|( - edges in build_edge_list(100, 2), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - if let Some(node) = g.node(0) { - let filtered_node = node.filter( - ExplodedEdgeFilter.property("int_prop").eq(v) - ).unwrap(); - let expected_filtered_g = build_filtered_graph(&edges, |vv| vv == v); - if filtered_node.degree() == 0 { - // should be filtered out - assert!(expected_filtered_g.node(0).is_none()) - } else { - assert_node_equal(filtered_node, expected_filtered_g.node(0).unwrap()) - } - } - }) - } - - #[test] - fn test_filter_materialise_window_is_consistent() { - proptest!(|( - edges in build_edge_list(100, 100), v in any::(), (start, end) in build_window() - )| { - let g = build_graph_from_edge_list(&edges); - let filter = ExplodedEdgeFilter.property("int_prop").eq(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let left = filtered.window(start, end); - let right = filtered.window(start, end).materialize().unwrap(); - assert_graph_equal(&left, &right); - }); - }) - } - - #[test] - fn test_persistent_graph() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, [("int_prop", 0i64)], None).unwrap(); - g.delete_edge(2, 1, 2, None).unwrap(); - g.add_edge(5, 1, 2, [("int_prop", 5i64)], None).unwrap(); - g.delete_edge(7, 1, 2, None).unwrap(); - - let edges = g - .node(1) - .unwrap() - .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) - .unwrap() - .edges() - .explode() - .collect(); - println!("{:?}", edges); - - assert_eq!(edges.len(), 1); - let gf = g - .filter(ExplodedEdgeFilter.property("int_prop").gt(1i64)) - .unwrap(); - let gfm = gf.materialize().unwrap(); - - assert_graph_equal(&gf, &gfm); // check materialise is consistent - } - - #[test] - fn test_persistent_graph_explode_semantics() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 0i64)], None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.add_edge(5, 0, 1, [("test", 5i64)], None).unwrap(); - g.delete_edge(10, 0, 1, None).unwrap(); - - let gf = g - .filter(ExplodedEdgeFilter.property("test").ne(2i64)) - .unwrap(); - assert_eq!( - gf.edges() - .explode() - .earliest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(0i64), Some(5i64)] - ); - assert_eq!( - gf.edges() - .explode() - .latest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(2i64), Some(10i64)] - ); - } - - #[test] - fn test_persistent_graph_materialise() { - proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::())| { - let g = build_graph_from_edge_list(&edges); - let g = g.persistent_graph(); - for (src, dst, t) in edge_deletions { - g.delete_edge(t, src, dst, None).unwrap(); - } - let filter = ExplodedEdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let gfm = filtered.materialize().unwrap(); - assert_graph_equal(&filtered, &gfm) - }); - }) - } - - #[test] - fn test_single_filtered_edge_persistent() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, [("test", 0i64)], None).unwrap(); - let gf = g - .filter(ExplodedEdgeFilter.property("test").gt(0i64)) - .unwrap(); - let gfm = gf.materialize().unwrap(); - dbg!(&gfm); - assert_eq!(gf.node(0).unwrap().out_degree(), 1); - assert_eq!(gfm.node(0).unwrap().out_degree(), 1); - assert_graph_equal(&gf, &gfm); - } - - #[test] - fn test_persistent_graph_materialise_window() { - proptest!(|(edges in build_edge_list(100, 100), edge_deletions in build_edge_deletions(100, 100), v in any::(), (start, end) in build_window())| { - let g = build_graph_from_edge_list(&edges); - let g = g.persistent_graph(); - for (src, dst, t) in edge_deletions { - g.delete_edge(t, src, dst, None).unwrap(); - } - let filter = ExplodedEdgeFilter.property("int_prop").gt(v); - assert_ok_or_missing_edges(&edges, g.window(start, end).filter(filter.clone()), |filtered| { - let gwfm = filtered.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&filtered, &gwfm); - }); - assert_ok_or_missing_edges(&edges, g.filter(filter.clone()), |filtered| { - let gfw = filtered.window(start, end); - let gfwm = gfw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gfw, &gfwm); - }); - }) - } - - #[test] - fn test_persistent_failure() { - let g = PersistentGraph::new(); - g.add_edge(-1, 0, 1, [("test", Prop::I32(-1))], None) - .unwrap(); - g.add_edge(0, 0, 1, [("test", Prop::I32(1))], None).unwrap(); - - let gwf = g - .filter(ExplodedEdgeFilter.property("test").gt(0)) - .unwrap() - .window(-1, 0); - assert_eq!(gwf.count_nodes(), 0); - assert_eq!(gwf.count_edges(), 0); - let gm = gwf.materialize().unwrap(); - - assert_persistent_materialize_graph_equal(&gwf, &gm); - - let gfw = g - .window(-1, 0) - .filter(ExplodedEdgeFilter.property("test").gt(0)) - .unwrap(); - assert_eq!(gfw.count_edges(), 0); - let gm = gfw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gfw, &gm); - } -} diff --git a/raphtory/tests/history_filter.rs b/raphtory/tests/history_filter.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/raphtory/tests/history_filter.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/raphtory/tests/node_property_filter.rs b/raphtory/tests/node_property_filter.rs deleted file mode 100644 index ca87e0e707..0000000000 --- a/raphtory/tests/node_property_filter.rs +++ /dev/null @@ -1,459 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use proptest::{arbitrary::any, proptest}; - use raphtory::{ - db::{ - api::view::filter_ops::{Filter, NodeSelect}, - graph::{ - assertions::assert_ok_or_missing_nodes, - graph::assert_edges_equal, - views::filter::model::{ - node_filter::{ops::NodeFilterOps, NodeFilter}, - property_filter::ops::PropertyFilterOps, - ComposableFilter, PropertyFilterFactory, - }, - }, - }, - prelude::*, - test_utils::{ - add_node_props, build_edge_list, build_graph_from_edge_list, build_node_props, - node_filtered_graph, - }, - }; - - #[test] - #[ignore] - // TODO: Enable this once fixed - fn test_node_filter_on_nodes() { - let g = Graph::new(); - g.add_node(0, "Jimi", [("band", "JH Experience")], None, None) - .unwrap(); - g.add_node(1, "John", [("band", "Dead & Company")], None, None) - .unwrap(); - g.add_node(2, "David", [("band", "Pink Floyd")], None, None) - .unwrap(); - - let filter_expr = NodeFilter::name() - .eq("John") - .and(NodeFilter.property("band").eq("Dead & Company")); - let filtered_nodes = g.nodes().filter(filter_expr).unwrap(); - - // filter_nodes doesn't filter the iterator, it only filters the view of the nodes which includes history, edges, etc. - assert_eq!( - filtered_nodes.name().collect::>(), - vec!["Jimi", "John", "David"] - ); - - // TODO: Bug! History isn't getting filtered - let res = filtered_nodes - .iter() - .map(|n| n.history()) - .collect::>(); - assert_eq!(res, vec![vec![], vec![1], vec![]]); - - // TODO: Bug! Properties aren't getting filtered - let res = filtered_nodes - .iter() - .map(|n| n.properties().get("band")) - .collect::>(); - assert_eq!(res, vec![None, Some(Prop::str("Dead & Company")), None]); - - g.add_edge(3, "John", "Jimi", NO_PROPS, None).unwrap(); - - let res = filtered_nodes - .iter() - .map(|n| n.out_neighbours().name().collect_vec()) - .collect::>(); - assert_eq!(res, vec![Vec::::new(), vec![], vec![]]); - } - - #[test] - fn test_node_property_filter_on_nodes() { - let g = Graph::new(); - g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); - g.add_node(0, 2, [("test", 2i64)], None, None).unwrap(); - g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); - g.add_node(1, 4, [("test", 4i64)], None, None).unwrap(); - - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); - g.add_edge(2, 3, 4, NO_PROPS, None).unwrap(); - g.add_edge(3, 4, 1, NO_PROPS, None).unwrap(); - - let n1 = g.node(1).unwrap(); - - assert_eq!( - n1.filter(NodeFilter.property("test").eq(1i64)) - .unwrap() - .edges() - .id() - .collect_vec(), - vec![] - ); - assert_eq!( - n1.filter(NodeFilter.property("test").eq(2i64)) - .unwrap() - .out_neighbours() - .id() - .collect_vec(), - vec![GID::U64(2)] - ); - - let n2 = g.node(2).unwrap(); - - assert_eq!( - n2.filter(NodeFilter.property("test").gt(1i64)) - .unwrap() - .neighbours() - .id() - .collect_vec(), - vec![GID::U64(3)] - ); - - assert_eq!( - n2.filter(NodeFilter.property("test").gt(0i64)) - .unwrap() - .neighbours() - .id() - .collect_vec(), - vec![GID::U64(1), GID::U64(3)] - ); - - let gp = g.persistent_graph(); - let n1p = gp.node(1).unwrap(); - - assert_eq!( - n1p.filter(NodeFilter.property("test").eq(1i64)) - .unwrap() - .edges() - .id() - .collect_vec(), - vec![] - ); - assert_eq!( - n1p.filter(NodeFilter.property("test").eq(2i64)) - .unwrap() - .out_neighbours() - .id() - .collect_vec(), - vec![GID::U64(2)] - ); - - let n2p = gp.node(2).unwrap(); - - assert_eq!( - n2p.filter(NodeFilter.property("test").gt(1i64)) - .unwrap() - .neighbours() - .id() - .collect_vec(), - vec![GID::U64(3)] - ); - - assert_eq!( - n2p.filter(NodeFilter.property("test").gt(0i64)) - .unwrap() - .neighbours() - .id() - .collect_vec(), - vec![GID::U64(1), GID::U64(3)] - ); - } - - #[test] - fn test_node_property_filter_path() { - let g = Graph::new(); - g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); - g.add_node(1, 2, [("test", 2i64)], None, None).unwrap(); - g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); - g.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); - - let filtered_nodes = g - .nodes() - .select(NodeFilter.property("test").gt(1i64)) - .unwrap(); - assert_eq!( - filtered_nodes - .out_neighbours() - .id() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, i)| i.sorted().collect_vec()) - .collect_vec(), - vec![vec![GID::U64(1), GID::U64(3)], vec![]] - ); - - assert_eq!( - filtered_nodes - .out_neighbours() - .degree() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, i)| i.collect_vec()) - .collect_vec(), - vec![vec![2, 2], vec![]] - ); - - let filtered_nodes_p = g - .persistent_graph() - .nodes() - .select(NodeFilter.property("test").gt(1i64)) - .unwrap(); - assert_eq!( - filtered_nodes_p - .out_neighbours() - .id() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, i)| i.sorted().collect_vec()) - .collect_vec(), - vec![vec![GID::U64(1), GID::U64(3)], vec![]] - ); - } - - #[test] - fn test_node_property_filter_on_graph() { - let g = Graph::new(); - g.add_node(0, 1, [("test", 1i64)], None, None).unwrap(); - g.add_node(1, 2, [("test", 2i64)], None, None).unwrap(); - g.add_node(1, 3, [("test", 3i64)], None, None).unwrap(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 3, NO_PROPS, None).unwrap(); - g.add_edge(1, 2, 1, NO_PROPS, None).unwrap(); - g.add_edge(1, 1, 3, NO_PROPS, None).unwrap(); - - let gf = g.filter(NodeFilter.property("test").eq(1i64)).unwrap(); - assert_eq!(gf.edges().id().collect_vec(), vec![]); - - let gf = g.filter(NodeFilter.property("test").gt(1i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - - let gf = g.filter(NodeFilter.property("test").lt(3i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(1), GID::U64(2)), (GID::U64(2), GID::U64(1))] - ); - - let gp = g.persistent_graph(); - let gf = gp.filter(NodeFilter.property("test").eq(1i64)).unwrap(); - assert_eq!(gf.edges().id().collect_vec(), vec![]); - - let gf = gp.filter(NodeFilter.property("test").gt(1i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - - let gf = gp.filter(NodeFilter.property("test").lt(3i64)).unwrap(); - assert_eq!( - gf.edges().id().collect_vec(), - vec![(GID::U64(1), GID::U64(2)), (GID::U64(2), GID::U64(1))] - ); - } - - #[test] - fn test_filter_gt() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").gt(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv > v).is_some() - }); - - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) - } - - #[test] - fn test_filter_ge() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").ge(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv >= v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) - } - - #[test] - fn test_filter_lt() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").lt(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv < v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) - } - - #[test] - fn test_filter_le() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").le(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv <= v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) - } - - #[test] - fn test_filter_eq() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").eq(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv == v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) - } - - #[test] - fn test_filter_ne() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), v in any::() - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").ne(v); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.filter(|&vv| *vv != v ).is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) - } - - #[test] - fn test_filter_is_some() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100), - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").is_some(); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.is_some() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) - } - - #[test] - fn test_filter_is_none() { - proptest!(|( - edges in build_edge_list(100, 100), nodes in build_node_props(100) - )| { - let g = build_graph_from_edge_list(&edges); - add_node_props(&g, &nodes); - let filter = NodeFilter.property("int_prop").is_none(); - let expected_g = node_filtered_graph(&edges, &nodes, |_, int_v| { - int_v.is_none() - }); - assert_ok_or_missing_nodes(&nodes, g.filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.edges()); - }); - assert_ok_or_missing_nodes(&nodes, g.persistent_graph().filter(filter.clone()), |filtered| { - assert_edges_equal(&filtered.edges(), &expected_g.persistent_graph().edges()); - // FIXME: history filtering not working properly - // assert_graph_equal(&filtered, &expected_g); - }); - }) - } - - #[test] - fn test_filter_is_none_simple_graph() { - let graph = Graph::new(); - graph - .add_node(1, 1, [("p1", 1), ("p2", 2)], Some("fire_nation"), None) - .unwrap(); - graph - .add_node(2, 1, [("p6", 6)], Some("fire_nation"), None) - .unwrap(); - graph - .add_node(2, 2, [("p4", 5)], Some("fire_nation"), None) - .unwrap(); - graph - .add_node(3, 3, [("p2", 4), ("p3", 3)], Some("water_tribe"), None) - .unwrap(); - - assert_eq!(graph.count_nodes(), 3); - - let filtered = graph.filter(NodeFilter.property("p2").is_none()).unwrap(); - let ids = filtered.nodes().name().collect_vec(); - - assert_eq!(ids, vec!["2"]); - } -} diff --git a/raphtory/tests/node_test.rs b/raphtory/tests/node_test.rs deleted file mode 100644 index 26250c0e9d..0000000000 --- a/raphtory/tests/node_test.rs +++ /dev/null @@ -1,437 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use raphtory::{ - db::{ - api::view::Filter, - graph::views::filter::model::{NodeViewFilterOps, ViewWrapOps}, - }, - prelude::*, - test_storage, - test_utils::test_graph, - }; - use raphtory_api::core::storage::arc_str::ArcStr; - use raphtory_core::storage::timeindex::AsTime; - use std::collections::HashMap; - - #[test] - fn test_earliest_time() { - let graph = Graph::new(); - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 1, NO_PROPS, None, None).unwrap(); - - // FIXME: Node add without properties not showing up (Issue #46) - test_graph(&graph, |graph| { - let view = graph.before(2); - assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 0); - assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 1); - - let view = graph.before(3); - assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 0); - assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 2); - - let view = graph.after(0); - assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 1); - assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 2); - - let view = graph.after(2); - assert_eq!(view.node(1), None); - assert_eq!(view.node(1), None); - - let view = graph.at(1); - assert_eq!(view.node(1).expect("v").earliest_time().unwrap(), 1); - assert_eq!(view.node(1).expect("v").latest_time().unwrap(), 1); - }); - } - - #[test] - fn test_properties() { - let graph = Graph::new(); - let props = [("test", "test")]; - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 1, props, None, None).unwrap(); - - // FIXME: Node add without properties not showing up (Issue #46) - test_graph(&graph, |graph| { - let v1 = graph.node(1).unwrap(); - let v1_w = graph.window(0, 1).node(1).unwrap(); - assert_eq!( - v1.properties().as_map(), - [(ArcStr::from("test"), Prop::str("test"))] - .into_iter() - .collect::>() - ); - assert_eq!(v1_w.properties().as_map(), HashMap::default()) - }); - } - - #[test] - fn test_property_additions() { - let graph = Graph::new(); - let props = [("test", "test")]; - let v1 = graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - v1.add_updates(2, props, None).unwrap(); - let v1_w = v1.window(0, 1); - assert_eq!( - v1.properties().as_map(), - props - .into_iter() - .map(|(k, v)| (ArcStr::from(k), v.into_prop())) - .collect::>() - ); - assert_eq!(v1_w.properties().as_map(), HashMap::default()) - } - - #[test] - fn test_metadata_additions() { - let g = Graph::new(); - let v1 = g.add_node(0, 1, NO_PROPS, None, None).unwrap(); - v1.add_metadata([("test", "test")]).unwrap(); - assert_eq!(v1.metadata().get("test"), Some("test".into())) - } - - #[test] - fn test_metadata_updates() { - let g = Graph::new(); - let v1 = g.add_node(0, 1, NO_PROPS, None, None).unwrap(); - v1.add_metadata([("test", "test")]).unwrap(); - v1.update_metadata([("test", "test2")]).unwrap(); - assert_eq!(v1.metadata().get("test"), Some("test2".into())) - } - - #[test] - #[ignore] // likely we don't want to handle it globally like this anymore, maybe we should introduce an explicit categorical property type? - fn test_string_deduplication() { - let g = Graph::new(); - let v1 = g - .add_node(0, 1, [("test1", "test"), ("test2", "test")], None, None) - .unwrap(); - let s1 = v1.properties().get("test1").unwrap_str(); - let s2 = v1.properties().get("test2").unwrap_str(); - - assert_eq!(s1.as_ptr(), s2.as_ptr()) - } - - #[test] - fn test_edge_history_and_timestamps() { - let graph = Graph::new(); - - // Add nodes - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - - // Add edges at different times - graph.add_edge(10, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(20, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(30, 2, 1, NO_PROPS, None).unwrap(); - graph.add_edge(5, 1, 3, NO_PROPS, None).unwrap(); // Earlier edge to same pair - - test_storage!(&graph, |graph| { - let node1 = graph.node(1).unwrap(); - - // Test edge_history (chronological order) - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![5, 10, 20, 30]); - - // Test edge_history_rev (reverse chronological order) - let history_rev: Vec<_> = node1.edge_history_rev().map(|(t, _)| t.t()).collect(); - assert_eq!(history_rev, vec![30, 20, 10, 5]); - - // Test earliest_edge_time - assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); - - // Test latest_edge_time - assert_eq!(node1.latest_edge_time().unwrap().t(), 30); - }); - } - - #[test] - fn test_edge_timestamps_with_windows() { - let graph = Graph::new(); - - // Add nodes - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - - // Add edges at different times - graph.add_edge(5, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(15, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(25, 2, 1, NO_PROPS, None).unwrap(); - graph.add_edge(35, 1, 3, NO_PROPS, None).unwrap(); - - test_graph(&graph, |graph| { - // Test window 0-20 - let windowed = graph.window(0, 20); - let node1 = windowed.node(1).unwrap(); - - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![5, 15]); - - assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); - assert_eq!(node1.latest_edge_time().unwrap().t(), 15); - - // Test window 10-30 - let windowed = graph.window(10, 30); - let node1 = windowed.node(1).unwrap(); - - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![15, 25]); - - assert_eq!(node1.earliest_edge_time().unwrap().t(), 15); - assert_eq!(node1.latest_edge_time().unwrap().t(), 25); - - // Test window after all edges - let windowed = graph.after(40); - assert_eq!(windowed.node(1), None); // Node has no edges in this window - }); - } - - #[test] - fn test_edge_timestamps_with_layers() { - let graph = Graph::new(); - - // Add nodes - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - - // Add edges on different layers - graph.add_edge(10, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(20, 1, 3, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(30, 2, 1, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(5, 1, 3, NO_PROPS, Some("layer2")).unwrap(); - - // Test all layers - let node1 = graph.node(1).unwrap(); - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![5, 10, 20, 30]); - assert_eq!(node1.earliest_edge_time().unwrap().t(), 5); - assert_eq!(node1.latest_edge_time().unwrap().t(), 30); - - // Test layer1 only - let layer1_graph = graph.layers(vec!["layer1"]).unwrap(); - let node1_layer1 = layer1_graph.node(1).unwrap(); - let history: Vec<_> = node1_layer1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![10, 30]); - assert_eq!(node1_layer1.earliest_edge_time().unwrap().t(), 10); - assert_eq!(node1_layer1.latest_edge_time().unwrap().t(), 30); - - // Test layer2 only - let layer2_graph = graph.layers(vec!["layer2"]).unwrap(); - let node1_layer2 = layer2_graph.node(1).unwrap(); - let history: Vec<_> = node1_layer2.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![5, 20]); - assert_eq!(node1_layer2.earliest_edge_time().unwrap().t(), 5); - assert_eq!(node1_layer2.latest_edge_time().unwrap().t(), 20); - } - - #[test] - fn test_edge_timestamps_overlapping_windows_and_layers() { - let graph = Graph::new(); - - // Add nodes - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 4, NO_PROPS, None, None).unwrap(); - - // Add edges with overlapping time ranges across multiple layers - graph.add_edge(5, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(10, 1, 3, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(15, 1, 4, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(20, 2, 1, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(25, 3, 1, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(30, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(35, 1, 4, NO_PROPS, Some("layer2")).unwrap(); - - test_graph(&graph, |graph| { - // Test overlapping window (8-22) with layer1 - let windowed_layer1 = graph.window(8, 22).layers(vec!["layer1"]).unwrap(); - let node1 = windowed_layer1.node(1).unwrap(); - - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![10, 20]); - assert_eq!(node1.earliest_edge_time().unwrap().t(), 10); - assert_eq!(node1.latest_edge_time().unwrap().t(), 20); - - // Test overlapping window (12-28) with layer2 - let windowed_layer2 = graph.window(12, 28).layers(vec!["layer2"]).unwrap(); - let node1 = windowed_layer2.node(1).unwrap(); - - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![15, 25]); - assert_eq!(node1.earliest_edge_time().unwrap().t(), 15); - assert_eq!(node1.latest_edge_time().unwrap().t(), 25); - - // Test overlapping window (18-32) with both layers - let windowed_both = graph - .window(18, 32) - .layers(vec!["layer1", "layer2"]) - .unwrap(); - let node1 = windowed_both.node(1).unwrap(); - - let history: Vec<_> = node1.edge_history().map(|(t, _)| t.t()).collect(); - assert_eq!(history, vec![20, 25, 30]); - assert_eq!(node1.earliest_edge_time().unwrap().t(), 20); - assert_eq!(node1.latest_edge_time().unwrap().t(), 30); - - // Test edge case: window with no edges for the node - let empty_window = graph.window(50, 60); - assert_eq!(empty_window.node(1), None); - }); - } - - #[test] - fn test_edge_timestamps_no_edges() { - let graph = Graph::new(); - - // Add a node but no edges - graph.add_node(10, 1, NO_PROPS, None, None).unwrap(); - - test_graph(&graph, |graph| { - let node1 = graph.node(1).unwrap(); - - // Node exists but has no edges - assert_eq!(node1.edge_history().count(), 0); - assert_eq!(node1.edge_history_rev().count(), 0); - assert_eq!(node1.earliest_edge_time(), None); - assert_eq!(node1.latest_edge_time(), None); - }); - } - - #[test] - fn test_node_layers() { - let graph = Graph::new(); - graph - .add_node(0, 1, [("a", "test")], None, Some("fire_nation")) - .unwrap(); - graph - .add_node(0, 2, NO_PROPS, None, Some("fire_nation")) - .unwrap(); - graph - .add_node(0, 3, [("b", 3)], None, Some("air_nomads")) - .unwrap(); - - let filter_expr = NodeFilter.layer("fire_nation").is_active(); - let ids = graph - .filter(filter_expr) - .unwrap() - .nodes() - .iter() - .map(|n| n.name()) - .collect::>(); - - assert_eq!(ids, vec!["1", "2"]); - } - - /// Nodes added without a layer (`layer=None`) go into STATIC_GRAPH_LAYER_ID and must be - /// visible as active in every layer-restricted view. - #[test] - fn test_unlayered_node_visible_in_all_layer_views() { - let graph = Graph::new(); - // Node 1 added without a layer — should appear in any layer view - graph.add_node(0, 1, NO_PROPS, None, None).unwrap(); - // Node 2 added with "fire_nation" — should only appear in that view - graph - .add_node(0, 2, NO_PROPS, None, Some("fire_nation")) - .unwrap(); - - let fire_view = graph - .filter(NodeFilter.layer("fire_nation").is_active()) - .unwrap(); - let mut ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); - ids.sort(); - // Both node 1 (unlayered/static) and node 2 (fire_nation) should be visible - assert_eq!( - ids, - vec!["1", "2"], - "unlayered node must appear in fire_nation view" - ); - } - - /// A node added with layer A must NOT appear in a filter restricted to layer B. - #[test] - fn test_layered_node_not_visible_in_other_layer_view() { - let graph = Graph::new(); - graph - .add_node(0, 1, NO_PROPS, None, Some("fire_nation")) - .unwrap(); - graph - .add_node(0, 2, NO_PROPS, None, Some("air_nomads")) - .unwrap(); - - let fire_view = graph - .filter(NodeFilter.layer("fire_nation").is_active()) - .unwrap(); - let ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); - assert_eq!( - ids, - vec!["1"], - "air_nomads node must not appear in fire_nation view" - ); - } - - /// Nodes added without a layer should appear in a windowed + layer-filtered view - /// as long as they have activity within the window. - #[test] - fn test_unlayered_node_visible_in_windowed_layer_view() { - let graph = Graph::new(); - graph.add_node(5, 1, NO_PROPS, None, None).unwrap(); // unlayered, t=5 - graph - .add_node(5, 2, NO_PROPS, None, Some("fire_nation")) - .unwrap(); // layered, t=5 - graph - .add_node(5, 3, NO_PROPS, None, Some("air_nomads")) - .unwrap(); - - // Window [0, 10) covers t=5; filter to fire_nation - let view = graph.window(0, 10); - let fire_view = view - .filter(NodeFilter.layer("fire_nation").is_active()) - .unwrap(); - let mut ids: Vec<_> = fire_view.nodes().iter().map(|n| n.name()).collect(); - ids.sort(); - assert_eq!( - ids, - vec!["1", "2"], - "unlayered node at t=5 must appear in windowed fire_nation view" - ); - - // Window [10, 20) does NOT cover t=5, so no nodes should be active - let view_out = graph.window(10, 20); - let fire_view_out = view_out - .filter(NodeFilter.layer("fire_nation").is_active()) - .unwrap(); - assert_eq!( - fire_view_out.count_nodes(), - 0, - "no nodes should be active outside the window" - ); - } - - /// Node history (timestamps) should be scoped to the requested layer, not bleed - /// across layers. - #[test] - fn test_node_history_scoped_to_layer() { - let graph = Graph::new(); - // Node 1 exists in fire_nation at t=1 and air_nomads at t=2 - graph - .add_node(1, 1, NO_PROPS, None, Some("fire_nation")) - .unwrap(); - graph - .add_node(2, 1, NO_PROPS, None, Some("air_nomads")) - .unwrap(); - - let fire_layer = graph.layers("fire_nation").unwrap(); - let node = fire_layer.node(1).unwrap(); - let history: Vec = node.history().t().collect(); - // Only the fire_nation timestamp should be visible - assert_eq!( - history, - vec![1], - "history must not include timestamps from other layers" - ); - } -} diff --git a/raphtory/tests/subgraph_tests.rs b/raphtory/tests/subgraph_tests.rs deleted file mode 100644 index 8ffc1b975f..0000000000 --- a/raphtory/tests/subgraph_tests.rs +++ /dev/null @@ -1,568 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use ahash::HashSet; - use itertools::Itertools; - use proptest::{proptest, sample::subsequence}; - use raphtory::{ - algorithms::{ - components::weakly_connected_components, motifs::triangle_count::triangle_count, - }, - db::graph::{graph::assert_graph_equal, views::deletion_graph::PersistentGraph}, - prelude::*, - test_storage, - test_utils::{build_graph, build_graph_strat}, - }; - use raphtory_storage::mutation::addition_ops::InternalAdditionOps; - use serde_json::json; - use std::collections::BTreeSet; - - #[test] - fn test_materialize_no_edges() { - let graph = Graph::new(); - - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 2, NO_PROPS, None, None).unwrap(); - - test_storage!(&graph, |graph| { - let sg = graph.subgraph([1, 2, 1]); // <- duplicated nodes should have no effect - - let actual = sg.materialize().unwrap().into_events().unwrap(); - assert_graph_equal(&actual, &sg); - }); - } - - #[test] - fn test_remove_degree1_triangle_count() { - let graph = Graph::new(); - let edges = vec![ - (1, 2, 1), - (1, 3, 2), - (1, 4, 3), - (3, 1, 4), - (3, 4, 5), - (3, 5, 6), - (4, 5, 7), - (5, 6, 8), - (5, 8, 9), - (7, 5, 10), - (8, 5, 11), - (1, 9, 12), - (9, 1, 13), - (6, 3, 14), - (4, 8, 15), - (8, 3, 16), - (5, 10, 17), - (10, 5, 18), - (10, 8, 19), - (1, 11, 20), - (11, 1, 21), - (9, 11, 22), - (11, 9, 23), - ]; - for (src, dst, ts) in edges { - graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let subgraph = graph.subgraph(graph.nodes().into_iter().filter(|v| v.degree() > 1)); - let ts = triangle_count(&subgraph, None); - let tg = triangle_count(graph, None); - assert_eq!(ts, tg) - }); - } - - #[test] - fn layer_materialize() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(0, 3, 4, NO_PROPS, Some("2")).unwrap(); - - test_storage!(&graph, |graph| { - let sg = graph.subgraph([1, 2]); - let sgm = sg.materialize().unwrap(); - assert_eq!( - sg.unique_layers().collect_vec(), - sgm.unique_layers().collect_vec() - ); - }); - } - - #[test] - fn test_cc() { - let graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 3, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); - graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(1, 3, 4, NO_PROPS, Some("1")).unwrap(); - let sg = graph.subgraph([0, 1, 3, 4]); - let cc = weakly_connected_components(&sg); - let groups = cc.groups(); - let group_sets = groups - .iter() - .map(|(_, g)| { - g.iter() - .map(|node| node.id()) - .sorted() - .collect::>() - }) - .collect::>(); - assert_eq!( - group_sets, - HashSet::from_iter([ - BTreeSet::from([GID::U64(0), GID::U64(1)]), - BTreeSet::from([GID::U64(3), GID::U64(4)]) - ]) - ); - } - - #[test] - fn test_layer_edges() { - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(1, 0, 1, NO_PROPS, Some("2")).unwrap(); - - assert_eq!( - graph.subgraph([0, 1]).edges().id().collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); - assert_eq!( - graph - .subgraph([0, 1]) - .valid_layers("1") - .edges() - .id() - .collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); - } - - pub mod test_filters_node_subgraph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::GraphTransformer, - views::{node_subgraph::NodeSubgraph, window_graph::WindowedGraph}, - }, - }, - prelude::{GraphViewOps, NodeViewOps, TimeOps}, - }; - use std::ops::Range; - - struct NodeSubgraphTransformer(Option>); - - impl GraphTransformer for NodeSubgraphTransformer { - type Return = NodeSubgraph; - fn apply(&self, graph: G) -> Self::Return { - let node_names: Vec = self - .0 - .clone() - .unwrap_or_else(|| graph.nodes().name().collect::>()); - graph.subgraph(node_names) - } - } - - struct WindowedNodeSubgraphTransformer(Option>, Range); - - impl GraphTransformer for WindowedNodeSubgraphTransformer { - type Return = NodeSubgraph>; - fn apply(&self, graph: G) -> Self::Return { - let graph = graph.window(self.1.start, self.1.end); - let node_names: Vec = self - .0 - .clone() - .unwrap_or_else(|| graph.nodes().name().collect::>()); - graph.subgraph(node_names) - } - } - - mod test_nodes_filters_node_subgraph { - use crate::test::test_filters_node_subgraph::{ - NodeSubgraphTransformer, WindowedNodeSubgraphTransformer, - }; - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - TestGraphVariants, TestVariants, - }, - views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, - }, - }, - prelude::{AdditionOps, NodeFilter}, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - fn init_graph(graph: G) -> G { - let nodes = vec![ - (6, "N1", vec![("p1", Prop::U64(2u64))]), - (7, "N1", vec![("p1", Prop::U64(1u64))]), - (6, "N2", vec![("p1", Prop::U64(1u64))]), - (7, "N2", vec![("p1", Prop::U64(2u64))]), - (8, "N3", vec![("p1", Prop::U64(1u64))]), - (9, "N4", vec![("p1", Prop::U64(1u64))]), - (5, "N5", vec![("p1", Prop::U64(1u64))]), - (6, "N5", vec![("p1", Prop::U64(2u64))]), - (5, "N6", vec![("p1", Prop::U64(1u64))]), - (6, "N6", vec![("p1", Prop::U64(1u64))]), - (3, "N7", vec![("p1", Prop::U64(1u64))]), - (5, "N7", vec![("p1", Prop::U64(1u64))]), - (3, "N8", vec![("p1", Prop::U64(1u64))]), - (4, "N8", vec![("p1", Prop::U64(2u64))]), - ]; - - for (id, name, props) in &nodes { - graph - .add_node(*id, name, props.clone(), None, None) - .unwrap(); - } - - graph - } - - #[test] - fn test_search_nodes_subgraph() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = ["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - NodeSubgraphTransformer(None), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - NodeSubgraphTransformer(None), - filter, - &expected_results, - TestVariants::All, - ); - - let node_names: Option> = - Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N3", "N4"]; - assert_filter_nodes_results( - init_graph, - NodeSubgraphTransformer(node_names.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - NodeSubgraphTransformer(node_names), - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_search_nodes_subgraph_w() { - // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let node_names: Option> = Some(vec!["N3".into()]); - let filter = NodeFilter.property("p1").gt(0u64); - let expected_results = vec!["N3"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_search_nodes_pg_w() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let node_names: Option> = - Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); - let filter = NodeFilter.property("p1").ge(1u64); - let expected_results = vec!["N2", "N3", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - - mod test_edges_filters_node_subgraph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestVariants, - }, - views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, - }, - }, - prelude::{AdditionOps, EdgeFilter}, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - use crate::test::test_filters_node_subgraph::{ - NodeSubgraphTransformer, WindowedNodeSubgraphTransformer, - }; - - fn init_graph(graph: G) -> G { - let edges = vec![ - (6, "N1", "N2", vec![("p1", Prop::U64(2u64))]), - (7, "N1", "N2", vec![("p1", Prop::U64(1u64))]), - (6, "N2", "N3", vec![("p1", Prop::U64(1u64))]), - (7, "N2", "N3", vec![("p1", Prop::U64(2u64))]), - (8, "N3", "N4", vec![("p1", Prop::U64(1u64))]), - (9, "N4", "N5", vec![("p1", Prop::U64(1u64))]), - (5, "N5", "N6", vec![("p1", Prop::U64(1u64))]), - (6, "N5", "N6", vec![("p1", Prop::U64(2u64))]), - (5, "N6", "N7", vec![("p1", Prop::U64(1u64))]), - (6, "N6", "N7", vec![("p1", Prop::U64(1u64))]), - (3, "N7", "N8", vec![("p1", Prop::U64(1u64))]), - (5, "N7", "N8", vec![("p1", Prop::U64(1u64))]), - (3, "N8", "N1", vec![("p1", Prop::U64(1u64))]), - (4, "N8", "N1", vec![("p1", Prop::U64(2u64))]), - ]; - - for (id, src, tgt, props) in &edges { - graph.add_edge(*id, src, tgt, props.clone(), None).unwrap(); - } - - graph - } - - #[test] - fn test_edges_filters() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - NodeSubgraphTransformer(None), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - NodeSubgraphTransformer(None), - filter, - &expected_results, - TestVariants::All, - ); - - let node_names: Option> = - Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = vec!["N3->N4", "N4->N5"]; - assert_filter_edges_results( - init_graph, - NodeSubgraphTransformer(node_names.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - NodeSubgraphTransformer(node_names), - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_filters_w() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let node_names: Option> = - Some(vec!["N2".into(), "N3".into(), "N4".into(), "N5".into()]); - let filter = EdgeFilter.property("p1").ge(1u64); - let expected_results = vec!["N2->N3", "N3->N4"]; - assert_filter_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_w() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let node_names: Option> = Some(vec![ - "N2".into(), - "N3".into(), - "N4".into(), - "N5".into(), - "N6".into(), - ]); - let filter = EdgeFilter.property("p1").lt(2u64); - let expected_results = vec!["N3->N4"]; - assert_filter_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowedNodeSubgraphTransformer(node_names, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - } - - #[test] - fn nodes_without_updates_are_filtered() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - let expected = Graph::new(); - expected.resolve_layer(None).unwrap(); - let subgraph = g.subgraph([0]); - assert_graph_equal(&subgraph, &expected); - } - - #[test] - fn materialize_proptest() { - proptest!(|(graph in build_graph_strat(10, 10, 10, 10, false), nodes in subsequence((0..10).collect::>(), 0..10))| { - let graph = Graph::from(build_graph(&graph)); - let subgraph = graph.subgraph(nodes); - assert_graph_equal(&subgraph, &subgraph.materialize().unwrap()); - }) - } - - #[test] - fn materialize_proptest_failure() { - let graph_f = serde_json::from_value(json!({"nodes":{},"edges":[[[1,1,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let graph = Graph::from(build_graph(&graph_f)); - let subgraph = graph.subgraph([1]); - let nodes = subgraph.default_layer().nodes().id().collect_vec(); - dbg!(nodes); - assert_eq!(subgraph.default_layer().count_nodes(), 0); - assert_eq!(subgraph.count_edges(), 1); - let materialised = subgraph.materialize().unwrap(); - assert_graph_equal(&subgraph, &materialised); - } - - #[test] - fn materialize_persistent_proptest() { - proptest!(|(graph in build_graph_strat(10, 10, 10, 10, true), nodes in subsequence((0..10).collect::>(), 0..10))| { - let graph = PersistentGraph::from(build_graph(&graph)); - let subgraph = graph.subgraph(nodes); - assert_graph_equal(&subgraph, &subgraph.materialize().unwrap()); - }) - } - - #[test] - fn test_subgraph_only_deletion() { - let g = PersistentGraph::new(); - g.delete_edge(0, 0, 1, None).unwrap(); - let sg = g.subgraph([0]); - let expected = PersistentGraph::new(); - expected.resolve_layer(None).unwrap(); - assert_graph_equal(&sg, &expected); - } -} diff --git a/raphtory/tests/test_deletions.rs b/raphtory/tests/test_deletions.rs deleted file mode 100644 index 7b49f7d531..0000000000 --- a/raphtory/tests/test_deletions.rs +++ /dev/null @@ -1,1186 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use proptest::{arbitrary::any, proptest, sample::subsequence}; - use raphtory::test_utils::{build_graph, build_graph_strat}; - use raphtory::{ - db::graph::{ - edge::EdgeView, - graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, - views::deletion_graph::PersistentGraph, - // views::deletion_graph::{GraphTimeSemanticsOps, PersistentGraph}, - }, - prelude::*, - test_storage, - }; - use raphtory_api::core::{ - entities::GID, - storage::timeindex::{AsTime, EventTime}, - }; - use raphtory_storage::mutation::addition_ops::InternalAdditionOps; - use rayon::ThreadPoolBuilder; - use std::ops::Range; - - #[test] - fn test_nodes() { - let g = PersistentGraph::new(); - - let edges = [ - (0, 1, 2, "assigned"), - (1, 1, 3, "assigned"), - (2, 4, 2, "has"), - (3, 4, 2, "has"), - (4, 5, 2, "blocks"), - (5, 4, 5, "has"), - (6, 6, 5, "assigned"), - ]; - - for (time, src, dst, layer) in edges { - g.add_edge(time, src, dst, [("added", Prop::I64(0))], Some(layer)) - .unwrap(); - } - - let nodes = g - .window(0, 1701786285758) - .layers(vec!["assigned", "has", "blocks"]) - .unwrap() - .nodes() - .into_iter() - .map(|vv| vv.name()) - .collect_vec(); - - assert_eq!(nodes, vec!["1", "2", "3", "4", "5", "6"]); - - let nodes = g - .at(1701786285758) - .layers(vec!["assigned", "has", "blocks"]) - .unwrap() - .nodes() - .into_iter() - .map(|vv| vv.name()) - .collect_vec(); - - assert_eq!(nodes, vec!["1", "2", "3", "4", "5", "6"]); - } - - #[test] - fn test_edge_deletions() { - let g = PersistentGraph::new(); - - g.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) - .unwrap(); - g.delete_edge(10, 0, 1, None).unwrap(); - - assert_eq!( - g.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - - assert_eq!( - g.window(1, 2).edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - - assert_eq!(g.window(1, 2).count_edges(), 1); - - assert_eq!(g.window(11, 12).count_edges(), 0); - - assert_eq!( - g.window(1, 2) - .edge(0, 1) - .unwrap() - .properties() - .get("added") - .unwrap_i64(), - 0 - ); - - assert!(g.window(11, 12).edge(0, 1).is_none()); - - assert_eq!( - g.window(1, 2) - .edge(0, 1) - .unwrap() - .properties() - .temporal() - .get("added") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - vec![(1, Prop::I64(0))] - ); - - assert_eq!(g.window(1, 2).node(0).unwrap().out_degree(), 1) - } - - #[test] - fn test_window_semantics() { - let g = PersistentGraph::new(); - g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); - g.delete_edge(10, 1, 2, None).unwrap(); - - assert_eq!(g.count_edges(), 1); - - assert_eq!(g.at(12).count_edges(), 0); - assert_eq!(g.at(11).count_edges(), 0); - assert_eq!(g.at(10).count_edges(), 0); - assert_eq!(g.at(9).count_edges(), 1); - assert_eq!(g.window(5, 9).count_edges(), 1); - assert_eq!(g.window(5, 10).count_edges(), 1); - assert_eq!(g.window(5, 11).count_edges(), 1); - assert_eq!(g.window(10, 12).count_edges(), 0); - assert_eq!(g.before(10).count_edges(), 1); - assert_eq!(g.after(10).count_edges(), 0); - } - - #[test] - fn test_timestamps() { - let g = PersistentGraph::new(); - let e = g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); - assert_eq!(e.earliest_time().unwrap(), 1); // time of first addition - assert_eq!(e.latest_time().map(|t| t.t()), Some(1)); // not deleted so alive forever - g.delete_edge(10, 1, 2, None).unwrap(); - assert_eq!(e.latest_time().unwrap(), 10); // deleted, so time of last deletion - - g.delete_edge(10, 3, 4, None).unwrap(); - let e = g.edge(3, 4).unwrap(); - assert_eq!(e.earliest_time().unwrap(), 10); // only deleted, earliest and latest time are the same - assert_eq!(e.latest_time().unwrap(), 10); - g.add_edge(1, 3, 4, [("test", "test")], None).unwrap(); - assert_eq!(e.latest_time().unwrap(), 10); - assert_eq!(e.earliest_time().unwrap(), 1); // added so timestamp is now the first addition - } - - #[test] - fn test_materialize_only_deletion() { - let g = PersistentGraph::new(); - g.delete_edge(1, 1, 2, None).unwrap(); - g.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); - g.delete_edge(5, 1, 2, None).unwrap(); - g.delete_edge(10, 1, 2, None).unwrap(); - assert_eq!( - g.window(0, 11).count_temporal_edges(), - g.count_temporal_edges() - ); - assert_graph_equal(&g.materialize().unwrap(), &g); - } - - #[test] - fn materialize_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { - let g = PersistentGraph::from(build_graph(&graph_f)); - let gm = g.materialize().unwrap(); - assert_graph_equal(&g, &gm); - }) - } - - #[test] - fn materialize_window_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = PersistentGraph::from(build_graph(&graph_f)); - let gw = g.window(w.start, w.end); - let gmw = gw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gw, &gmw); - }) - } - - #[test] - fn test_multilayer_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(10, 0, 0, NO_PROPS, Some("a")).unwrap(); - println!("all: {g:?}"); - let gw = g.window(1, 10); - println!("windowed nodes: {:?}", gw.nodes()); - let gm = gw.materialize().unwrap(); - - println!("materialized: {:?}", gm); - - assert_eq!(gw.valid_layers("a").count_nodes(), 0); - assert_eq!(gm.valid_layers("a").count_nodes(), 0); - - let expected = PersistentGraph::new(); - expected.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); - expected.resolve_layer(Some("a")).unwrap(); // empty layer exists - - println!("expected: {:?}", expected); - assert_persistent_materialize_graph_equal(&gw, &expected); - - assert_persistent_materialize_graph_equal(&gw, &gm); - } - - #[test] - fn test_multilayer_window2() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); - let gw = g.window(1, i64::MAX); - let gm = gw.materialize().unwrap(); - println!("original: {g:?}"); - assert_eq!(g.default_layer().count_nodes(), 1); - println!("materialized: {gm:?}"); - assert_eq!(gm.default_layer().count_nodes(), 1); - - assert_persistent_materialize_graph_equal(&gw, &gm.clone().into_persistent().unwrap()); - assert_persistent_materialize_graph_equal(&gw, &gm); - } - - #[test] - fn test_deletion_at_window_start() { - let g = PersistentGraph::new(); - g.add_edge(2, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(0, 0, 1, None).unwrap(); - - // deletion at start of window is not part of the view - let gw = g.window(0, 1); - assert!(gw.is_empty()); - - let gw = g.window(0, 3); - assert_eq!(gw.node(0).unwrap().earliest_time().unwrap().t(), 2); - assert_eq!(gw.node(1).unwrap().earliest_time().unwrap().t(), 2); - } - - #[test] - fn materialize_window_layers_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), l in subsequence(&["a", "b"], 0..=2), num_threads in 1..=16usize)| { - let pool = ThreadPoolBuilder::new().num_threads(num_threads).build().unwrap(); - pool.install(|| { - let g = PersistentGraph::from(build_graph(&graph_f)); - let glw = g.valid_layers(l.clone()).window(w.start, w.end); - let gmlw = glw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&glw, &gmlw); - }) - - }) - } - - #[test] - fn materialize_window_multilayer() { - let g = PersistentGraph::new(); - g.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); - g.delete_edge(3, 0, 0, Some("a")).unwrap(); - let w = 0..10; - let glw = g.valid_layers("a").window(w.start, w.end); - let gmlw = glw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&glw, &gmlw); - } - - #[test] - fn test_materialize_deleted_edge() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(1, 0, 1, None).unwrap(); - g.delete_edge(5, 0, 1, None).unwrap(); - - let gw = g.window(2, 10); - - let gm = gw.materialize().unwrap(); - - assert_graph_equal(&gw, &gm); - } - - #[test] - fn test_addition_deletion_multilayer_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); - g.delete_edge(0, 0, 0, None).unwrap(); - let gw = g.window(0, 0).valid_layers("a"); - let expected_gw = PersistentGraph::new(); - expected_gw.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gw, &expected_gw); - let gwm = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gwm); - } - - #[test] - fn materialize_broken_time() { - let g = PersistentGraph::new(); - g.add_edge( - -7868307470600541330, - 0, - 0, - [("test", Prop::map([("x", "y")]))], - Some("a"), - ) - .unwrap(); - g.add_edge( - -8675512464616562592, - 0, - 0, - [("test", Prop::map([("z", "hi")]))], - None, - ) - .unwrap(); - g.edge(0, 0) - .unwrap() - .add_metadata([("other", "b")], None) - .unwrap(); - let gw = g.window(-7549523977641994620, -995047120251067629); - assert_persistent_materialize_graph_equal(&gw, &gw.materialize().unwrap()) - } - - #[test] - fn test_materialize_window_start_before_node_add() { - let g = PersistentGraph::new(); - g.add_node(-1, 0, [("test", "test")], None, None).unwrap(); - g.add_node(5, 0, [("test", "blob")], None, None).unwrap(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - let gw = g.window(-5, 8); - let gmw = gw.materialize().unwrap(); - assert_graph_equal(&gw, &gmw); - } - - #[test] - fn test_materialize_edge_metadata() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.add_metadata([("test", "test")], None).unwrap(); - g.delete_edge(1, 1, 2, None).unwrap(); - - let gw = g.after(1); - let gmw = gw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gw, &gmw); - } - - #[test] - fn test_metadata_multiple_layers() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("a")).unwrap(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.add_metadata([("test", "test")], None).unwrap(); - g.delete_edge(1, 1, 2, None).unwrap(); - assert_eq!( - g.edge(1, 2).unwrap().metadata().as_vec(), - [("test".into(), Prop::map([("_default", "test")]))] - ); - let gw = g.after(1); - assert!(gw - .edge(1, 2) - .unwrap() - .metadata() - .iter_filtered() - .next() - .is_none()); - let g_before = g.before(1); - assert_eq!( - g_before.edge(1, 2).unwrap().metadata().as_vec(), - [("test".into(), Prop::map([("_default", "test")]))] - ); - } - - #[test] - fn test_materialize_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.delete_edge(10, 1, 2, None).unwrap(); - - let gm = g - .window(3, 5) - .materialize() - .unwrap() - .into_persistent() - .unwrap(); - assert_persistent_materialize_graph_equal(&g.window(3, 5), &gm); // ignore start of window as it has different updates by design - } - - #[test] - fn test_materialize_window_node_props() { - let g = Graph::new(); - g.add_node(0, 1, [("test", "test")], None, None).unwrap(); - - test_storage!(&g, |g| { - let g = g.persistent_graph(); - - let wg = g.window(3, 5); - let mg = wg.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&wg, &mg); - }); - } - - #[test] - fn test_exploded_latest_time() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.delete_edge(10, 1, 2, None).unwrap(); - assert_eq!(e.latest_time().unwrap().t(), 10); - assert_eq!( - e.explode() - .latest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - vec![Some(10)] - ); - } - - #[test] - fn test_exploded_window() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - for t in [5, 10, 15] { - e.add_updates(t, NO_PROPS, None).unwrap(); - } - assert_eq!( - e.after(2).explode().time().flatten().collect_vec(), - [3, 5, 10, 15] - ); - } - - #[test] - fn test_edge_properties() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, [("test", "test")], None).unwrap(); - assert_eq!(e.properties().get("test").unwrap_str(), "test"); - e.delete(10, None).unwrap(); - assert_eq!(e.properties().get("test").unwrap_str(), "test"); - assert_eq!(e.at(10).properties().get("test"), None); - e.add_updates(11, [("test", "test11")], None).unwrap(); - assert_eq!( - e.window(10, 12).properties().get("test").unwrap_str(), - "test11" - ); - assert_eq!( - e.window(5, 12) - .properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - vec![(5, Prop::str("test")), (11i64, Prop::str("test11"))], - ); - } - - #[test] - fn test_multiple_edge_properties() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test1", "test1")], None).unwrap(); - g.add_edge(1, 0, 1, [("test2", "test2")], None).unwrap(); - - let e = g.edge(0, 1).unwrap(); - assert_eq!(e.properties().get("test1").unwrap_str(), "test1"); - assert_eq!(e.properties().get("test2").unwrap_str(), "test2"); - - let ew = e.window(1, 10); - assert_eq!(ew.properties().get("test1").unwrap_str(), "test1"); - assert_eq!(ew.properties().get("test2").unwrap_str(), "test2"); - } - - #[test] - fn test_edge_history() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.delete(5, None).unwrap(); - e.add_updates(10, NO_PROPS, None).unwrap(); - assert_eq!(e.history(), [0, 10]); - assert_eq!(e.after(1).history(), [10]); - assert!(e.window(1, 4).history().is_empty()); - - // exploded edge still exists - assert_eq!( - e.window(1, 4) - .explode() - .earliest_time() - .flatten() - .collect_vec(), - [1] - ); - } - - #[test] - fn test_ordering_of_addition_and_deletion() { - let g = PersistentGraph::new(); - - //deletion before addition (deletion has no effect and edge exists (1, inf) - g.delete_edge(1, 1, 2, None).unwrap(); - let e_1_2 = g.add_edge(1, 1, 2, [("test", "test")], None).unwrap(); - - //deletion after addition (edge exists only at 2) - let e_3_4 = g.add_edge(2, 3, 4, [("test", "test")], None).unwrap(); - g.delete_edge(2, 3, 4, None).unwrap(); - - assert_eq!(e_1_2.at(0).properties().get("test"), None); - assert_eq!(e_1_2.at(1).properties().get("test").unwrap_str(), "test"); - assert_eq!(e_1_2.at(2).properties().get("test").unwrap_str(), "test"); - - assert_eq!(e_3_4.at(0).properties().get("test"), None); - assert_eq!(e_3_4.at(2).properties().get("test"), None); - assert_eq!(e_3_4.at(3).properties().get("test"), None); - - assert!(!g.window(0, 1).has_edge(1, 2)); - assert!(!g.window(0, 2).has_edge(3, 4)); - assert!(g.window(1, 2).has_edge(1, 2)); - assert!(!g.window(2, 3).has_edge(3, 4)); // deleted at start of window - assert!(!g.window(3, 4).has_edge(3, 4)); - } - - #[test] - fn test_deletions() { - let edges = [ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - let g = PersistentGraph::new(); - for (t, s, d) in edges.iter() { - g.add_edge(*t, *s, *d, NO_PROPS, None).unwrap(); - } - g.delete_edge(10, edges[0].1, edges[0].2, None).unwrap(); - - for (t, s, d) in &edges { - assert!(g.at(*t).has_edge(*s, *d)); - } - assert!(!g.after(10).has_edge(edges[0].1, edges[0].2)); - for (_, s, d) in &edges[1..] { - assert!(g.after(10).has_edge(*s, *d)); - } - assert_eq!( - g.edge(edges[0].1, edges[0].2) - .unwrap() - .explode() - .latest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(10)] - ); - } - - fn check_valid<'graph, G: GraphViewOps<'graph>>(e: &EdgeView) { - assert!(e.is_valid()); - assert!(!e.is_deleted()); - assert!(e.graph.has_edge(e.src(), e.dst())); - assert!(e.graph.edge(e.src(), e.dst()).is_some()); - } - - fn check_deleted<'graph, G: GraphViewOps<'graph>>(e: &EdgeView) { - assert!(!e.is_valid()); - assert!(e.is_deleted()); - let t = e.latest_time().map(|t| t.t()).unwrap_or(i64::MAX); - let g = e.graph.at(t); // latest view of the graph - assert!(!g.has_edge(e.src(), e.dst())); - assert!(g.edge(e.src(), e.dst()).is_none()); - } - - #[test] - fn test_deletion_multiple_layers() { - let g = PersistentGraph::new(); - - g.add_edge(1, 1, 2, NO_PROPS, Some("1")).unwrap(); - g.delete_edge(2, 1, 2, Some("2")).unwrap(); - g.delete_edge(10, 1, 2, Some("1")).unwrap(); - g.add_edge(10, 1, 2, NO_PROPS, Some("2")).unwrap(); - - let e = g.edge(1, 2).unwrap(); - let e_layer_1 = e.layers("1").unwrap(); - let e_layer_2 = e.layers("2").unwrap(); - - assert!(!g.at(0).has_edge(1, 2)); - check_deleted(&e.at(0)); - for t in 1..13 { - assert!(g.at(t).has_edge(1, 2)); - check_valid(&e.at(t)); - } - - check_valid(&e); - - check_deleted(&e_layer_1); - check_deleted(&e_layer_1.at(10)); - check_valid(&e_layer_1.at(9)); - check_valid(&e_layer_1.at(1)); - check_deleted(&e_layer_1.at(0)); - - check_valid(&e_layer_2); - check_deleted(&e_layer_2.at(9)); - check_valid(&e_layer_2.at(10)); - } - - #[test] - fn test_materialize_node_type() { - let g = PersistentGraph::new(); - g.delete_edge(0, 0, 0, None).unwrap(); - g.node(0).unwrap().set_node_type("test").unwrap(); - assert_graph_equal(&g, &g.materialize().unwrap()); - } - - #[test] - fn test_edge_is_valid() { - let g = PersistentGraph::new(); - - g.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - let e = g.edge(1, 2).unwrap(); - check_deleted(&e.before(1)); - check_valid(&e.after(1)); - check_valid(&e); - - g.add_edge(2, 1, 2, NO_PROPS, Some("1")).unwrap(); - check_valid(&e); - - g.delete_edge(3, 1, 2, Some("1")).unwrap(); - check_valid(&e); - check_deleted(&e.layers("1").unwrap()); - check_deleted(&e.layers("1").unwrap().at(3)); - check_deleted(&e.layers("1").unwrap().after(3)); - check_valid(&e.layers("1").unwrap().before(3)); - check_valid(&e.default_layer()); - - g.delete_edge(4, 1, 2, None).unwrap(); - check_deleted(&e); - check_deleted(&e.layers("1").unwrap()); - check_deleted(&e.default_layer()); - - g.add_edge(5, 1, 2, NO_PROPS, None).unwrap(); - check_valid(&e); - check_valid(&e.default_layer()); - check_deleted(&e.layers("1").unwrap()); - } - - /// Each layer is handled individually, deletions in one layer do not have an effect on other layers. - /// Layers that have only deletions are ignored - #[test] - fn test_explode_multiple_layers() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); - g.add_edge(1, 1, 2, NO_PROPS, Some("2")).unwrap(); - g.delete_edge(1, 1, 2, Some("1")).unwrap(); - g.delete_edge(2, 1, 2, Some("2")).unwrap(); - g.delete_edge(3, 1, 2, Some("3")).unwrap(); - - let e = g.edge(1, 2).unwrap(); - assert_eq!(e.explode().iter().count(), 2); - assert_eq!(e.before(4).explode().iter().count(), 2); - assert_eq!(e.window(1, 3).explode().iter().count(), 1); - assert_eq!(e.window(2, 3).explode().iter().count(), 0); - } - - #[test] - fn test_edge_latest_time() { - let g = PersistentGraph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e.delete(2, None).unwrap(); - assert_eq!(e.at(2).earliest_time(), None); - assert_eq!(e.at(2).latest_time(), None); - assert!(e.at(2).is_deleted()); - assert_eq!(e.latest_time().unwrap().t(), 2); - e.add_updates(4, NO_PROPS, None).unwrap(); - assert_eq!(e.latest_time().unwrap().t(), 4); - - assert_eq!(e.window(0, 3).latest_time().unwrap().t(), 2); - } - - #[test] - fn test_node_property_semantics() { - let g = PersistentGraph::new(); - let _v = g - .add_node(1, 1, [("test_prop", "test value")], None, None) - .unwrap(); - let v = g - .add_node(11, 1, [("test_prop", "test value 2")], None, None) - .unwrap(); - let v_from_graph = g.at(10).node(1).unwrap(); - assert_eq!(v.properties().get("test_prop").unwrap_str(), "test value 2"); - assert_eq!( - v.at(10).properties().get("test_prop").unwrap_str(), - "test value" - ); - assert_eq!( - v.at(11).properties().get("test_prop").unwrap_str(), - "test value 2" - ); - assert_eq!( - v_from_graph.properties().get("test_prop").unwrap_str(), - "test value" - ); - - assert_eq!( - v.before(11).properties().get("test_prop").unwrap_str(), - "test value" - ); - - assert_eq!( - v.properties() - .temporal() - .get("test_prop") - .unwrap() - .history(), - [1, 11] - ); - assert_eq!( - v_from_graph - .properties() - .temporal() - .get("test_prop") - .unwrap() - .history(), - [10] - ); - - assert_eq!(v_from_graph.earliest_time().map(|t| t.t()), Some(10)); - assert_eq!(v.earliest_time().map(|t| t.t()), Some(1)); - assert_eq!(v.at(10).earliest_time().map(|t| t.t()), Some(10)); - assert_eq!(v.at(10).latest_time().map(|t| t.t()), Some(10)); - assert_eq!(v.latest_time().map(|t| t.t()), Some(11)); - } - - #[test] - fn test_event_graph() { - let pg = PersistentGraph::new(); - pg.add_edge(0, 0, 1, [("added", Prop::I64(0))], None) - .unwrap(); - pg.delete_edge(10, 0, 1, None).unwrap(); - assert_eq!( - pg.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - - let g = pg.event_graph(); - assert_eq!( - g.edges().id().collect::>(), - vec![(GID::U64(0), GID::U64(1))] - ); - } - - #[test] - fn test_exploded_latest_time_deleted() { - let g = PersistentGraph::new(); - g.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - g.delete_edge(1, 1, 2, None).unwrap(); - - assert_eq!( - g.edge(1, 2) - .unwrap() - .explode() - .latest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(1)] - ); - assert_eq!( - g.edge(1, 2) - .unwrap() - .explode() - .earliest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(), - [Some(1)] - ) - } - - #[test] - fn test_empty_window_has_no_nodes() { - let g = PersistentGraph::new(); - g.add_node(1, 1, NO_PROPS, None, None).unwrap(); - assert_eq!(g.window(2, 2).count_nodes(), 0); - assert_eq!(g.window(1, 1).count_nodes(), 0); - assert_eq!(g.window(0, 0).count_nodes(), 0); - } - - // #[test] - // fn test_earliest_latest_only_deletion() { - // let g = PersistentGraph::new(); - // g.delete_edge(1, 1, 2, None).unwrap(); - // let gw = g.window(0, 1); - // assert_eq!(gw.earliest_time(), Some(0)); - // assert_eq!(gw.latest_time(), Some(0)); - // } - - #[test] - fn test_earliest_latest_time_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - - assert_eq!(g.window(-1, 0).earliest_time(), None); - assert_eq!(g.window(-1, 0).latest_time(), None); - } - - #[test] - fn test_node_earliest_time_window() { - let g = PersistentGraph::new(); - g.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - g.add_edge(4, 1, 3, NO_PROPS, None).unwrap(); - - assert_eq!( - g.window(2, 7).node(3).unwrap().earliest_time().unwrap().t(), - 4 - ); - assert_eq!( - g.window(2, 7).node(1).unwrap().earliest_time().unwrap().t(), - 2 - ); - } - - #[test] - fn test_graph_property_semantics() { - let g = PersistentGraph::new(); - g.add_properties(1, [("weight", 10i64)]).unwrap(); - g.add_properties(3, [("weight", 20i64)]).unwrap(); - let prop = g.properties().temporal().get("weight").unwrap(); - - assert_eq!( - prop, - [(EventTime::new(1, 0), 10i64), (EventTime::new(3, 1), 20i64)] - ); - - let prop = g - .window(5, 7) - .properties() - .temporal() - .get("weight") - .unwrap(); - assert_eq!(prop, [(EventTime::new(5, 0), 20i64)]) - } - - #[test] - fn test_exploded_edge_window() { - let g = PersistentGraph::new(); - g.add_edge(1, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(3, 0, 1, [("test", 3i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); - - let prop_values = g - .window(2, 5) - .edges() - .explode() - .properties() - .map(|props| props.get("test").unwrap_i64()) - .collect::>(); - assert_eq!(prop_values, [1, 3, 4]); - } - - /// This is a weird edge case - /// - /// An edge deletion creates the corresponding nodes so they should be alive from that point on. - /// We might consider changing the exact semantics here... - #[test] - fn test_node_earliest_latest_time_edge_deletion_only() { - let g = PersistentGraph::new(); - g.delete_edge(10, 0, 1, None).unwrap(); - assert_eq!(g.node(0).unwrap().earliest_time().unwrap().t(), 10); - assert_eq!(g.node(1).unwrap().earliest_time().unwrap().t(), 10); - assert_eq!(g.node(0).unwrap().latest_time().unwrap().t(), 10); - assert_eq!(g.node(1).unwrap().latest_time().unwrap().t(), 10); - } - - /// For an edge the earliest time is the time of the first update (either addition or deletion) - /// - /// The latest time is the time stamp of the last deletion if the last update is a deletion or - /// i64::MAX otherwise. - #[test] - fn test_edge_earliest_latest_time_edge_deletion_only() { - let g = PersistentGraph::new(); - g.delete_edge(10, 0, 1, None).unwrap(); - assert_eq!(g.edge(0, 1).unwrap().earliest_time().unwrap().t(), 10); - assert_eq!(g.edge(0, 1).unwrap().latest_time().unwrap().t(), 10); - } - - /// Repeated deletions are ignored, only the first one is relevant. Subsequent deletions do not - /// create exploded edges - #[test] - fn test_repeated_deletions() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(2, 0, 1, None).unwrap(); - g.delete_edge(4, 0, 1, None).unwrap(); - - let e = g.edge(0, 1).unwrap(); - let ex_earliest_t = e - .explode() - .earliest_time() - .map(|t_opt| t_opt.map(|t| t.t())) - .collect_vec(); - assert_eq!(ex_earliest_t, [Some(0)]); - } - - /// Only additions create exploded edges - #[test] - fn test_only_deletions() { - let g = PersistentGraph::new(); - g.delete_edge(2, 0, 1, None).unwrap(); - g.delete_edge(4, 0, 1, None).unwrap(); - - let e = g.edge(0, 1).unwrap(); - let ex_earliest_t = e.explode().earliest_time().collect_vec(); - assert_eq!(ex_earliest_t, []); - } - - /// Deletions only bring an edge into the window if they fall inside the window, not if they fall - /// onto the start of the window. The edge is already considered deleted at the time of the - /// deletion event, not at the next step. - #[test] - fn test_only_deletions_window() { - let g = PersistentGraph::new(); - g.delete_edge(1, 0, 1, None).unwrap(); - - // the edge exists - assert!(g.has_edge(0, 1)); - - // the deletion falls inside the window so the edge exists - assert!(g.window(0, 2).has_edge(0, 1)); - - // the deletion falls on the start of the window so the edge is already deleted and does not exist - assert!(!g.window(1, 2).has_edge(0, 1)); - - // windows that don't contain the deletion event don't have the edge - assert!(!g.window(0, 1).has_edge(0, 1)); - assert!(!g.window(2, 3).has_edge(0, 1)); - } - - #[test] - fn test_multiple_updates_at_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(0, 0, 1, [("test", 2i64)], None).unwrap(); - - let e = g.edge(0, 1).unwrap(); - assert_eq!(e.properties().get("test").unwrap_i64(), 2); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(0, Prop::I64(1)), (0, Prop::I64(2))] - ); - - assert_eq!(e.at(0).properties().get("test").unwrap_i64(), 2); - assert_eq!( - e.at(0) - .properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(0, Prop::I64(1)), (0, Prop::I64(2))] - ); - - assert_eq!( - e.at(0) - .explode() - .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [1, 2] - ); - assert_eq!(e.at(0).history(), [0, 0]); - assert_eq!(e.history(), [0, 0]); - } - - #[test] - fn no_persistence_if_updated_at_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); - - let e = g.edge(0, 1).unwrap().window(2, 5); - - assert_eq!(e.properties().get("test").unwrap_i64(), 4); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(2, Prop::I64(2)), (4, Prop::I64(4))] - ); - assert_eq!( - e.explode() - .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [2, 4] - ); - assert_eq!(e.history(), [2, 4]); - assert!(e.deletions().is_empty()); - } - - #[test] - fn persistence_if_not_updated_at_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); - - let e = g.edge(0, 1).unwrap().window(1, 5); - - assert_eq!(e.properties().get("test").unwrap_i64(), 4); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(1, Prop::I64(1)), (2, Prop::I64(2)), (4, Prop::I64(4))] - ); - assert_eq!( - e.explode() - .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [1, 2, 4] - ); - assert_eq!(e.history(), [2, 4]); // is this actually what we want? - assert!(e.deletions().is_empty()); - assert_eq!(g.window(1, 5).count_temporal_edges(), 3); - assert_eq!(g.window(2, 5).count_temporal_edges(), 2); - assert_eq!(g.window(3, 5).count_temporal_edges(), 2); - } - - #[test] - fn no_persistence_if_deleted() { - let g = PersistentGraph::new(); - g.add_edge(-1, 0, 1, [("test", 1i64)], None).unwrap(); - g.delete_edge(0, 0, 1, None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); - - let e = g.edge(0, 1).unwrap().window(1, 5); - - assert_eq!(e.properties().get("test").unwrap_i64(), 4); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(2, Prop::I64(2)), (4, Prop::I64(4))] - ); - assert_eq!( - e.explode() - .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [2, 4] - ); - assert_eq!(e.history(), [2, 4]); - assert!(e.deletions().is_empty()); - assert_eq!(g.window(0, 5).count_temporal_edges(), 2); - assert_eq!(g.window(1, 5).count_temporal_edges(), 2); - assert_eq!(g.window(3, 5).count_temporal_edges(), 2); - } - - #[test] - fn test_deletion_at_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, [("test", 1i64)], None).unwrap(); - g.add_edge(2, 0, 1, [("test", 2i64)], None).unwrap(); - g.delete_edge(2, 0, 1, None).unwrap(); - g.add_edge(2, 0, 1, [("test", 3i64)], None).unwrap(); - g.add_edge(4, 0, 1, [("test", 4i64)], None).unwrap(); - - let e = g.edge(0, 1).unwrap().window(2, 5); - - assert_eq!(e.properties().get("test").unwrap_i64(), 4); - assert_eq!( - e.properties() - .temporal() - .get("test") - .unwrap() - .iter() - .map(|(t, p)| (t.t(), p)) - .collect_vec(), - [(2, Prop::I64(3)), (4, Prop::I64(4))] - ); - - assert_eq!( - e.explode() - .properties() - .map(|p| p.get("test").unwrap_i64()) - .collect_vec(), - [3, 4] - ); - - assert!(e.deletions().is_empty()); - assert_eq!(e.history(), [2, 4]); - assert_eq!(g.window(1, 5).count_temporal_edges(), 4); - assert_eq!(g.window(2, 5).count_temporal_edges(), 2); - assert_eq!(g.window(3, 5).count_temporal_edges(), 2); - } - - #[test] - fn multiple_node_updates_at_same_time() { - let g = PersistentGraph::new(); - - g.add_node(1, 1, [("prop1", 1)], None, None).unwrap(); - g.add_node(2, 1, [("prop1", 2)], None, None).unwrap(); - g.add_node(2, 1, [("prop1", 3)], None, None).unwrap(); - g.add_node(8, 1, [("prop1", 4)], None, None).unwrap(); - g.add_node(9, 1, [("prop1", 5)], None, None).unwrap(); - - assert_eq!( - g.window(2, 10) - .node(1) - .unwrap() - .properties() - .temporal() - .get("prop1") - .unwrap() - .values() - .collect_vec(), - [Prop::I32(2), Prop::I32(3), Prop::I32(4), Prop::I32(5)] - ) - } - - #[test] - fn filtering_all_layers_keeps_explicitly_added_nodes() { - let g = PersistentGraph::new(); - g.add_node(0, 0, [("prop1", false)], None, None).unwrap(); - let view = g.valid_layers(Layer::None).window(0, 1); - assert_eq!(view.count_nodes(), 1); - assert_eq!(view.count_edges(), 0); - assert_graph_equal(&view, &view.materialize().unwrap()) - } - - #[test] - fn filtering_all_layers_removes_other_nodes() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - let view = g.valid_layers(Layer::None).window(0, 1); - assert_eq!(view.count_nodes(), 0); - assert_eq!(view.count_edges(), 0); - assert_graph_equal(&view, &view.materialize().unwrap()) - } - - #[test] - fn deletions_window_has_exclusive_start() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(2, 0, 1, None).unwrap(); - let e = g.edge(0, 1).unwrap(); - assert!(e.is_active()); // has updates - assert!(!e.is_valid()); // last update is a deletion - assert!(e.is_deleted()); - - assert!(e.window(0, 1).is_active()); // addition in window - assert!(e.window(0, 1).is_valid()); // not deleted - assert!(!e.window(0, 1).is_deleted()); - - assert!(e.window(1, 3).is_active()); // deletion in window - assert!(!e.window(1, 3).is_valid()); - assert!(e.window(1, 3).is_deleted()); - - assert!(!e.window(1, 2).is_active()); // no updates in window - assert!(e.window(1, 2).is_valid()); // deletion not in window (exclusive end) - assert!(!e.window(1, 2).is_deleted()); - - assert!(!e.window(2, 3).is_active()); // deletion at start of window are not included - assert!(!e.window(2, 3).is_valid()); - assert!(e.window(2, 3).is_deleted()); - assert!(!e.latest().is_active()); // this is the same as above - } -} diff --git a/raphtory/tests/test_edge.rs b/raphtory/tests/test_edge.rs deleted file mode 100644 index 2bb247e03b..0000000000 --- a/raphtory/tests/test_edge.rs +++ /dev/null @@ -1,193 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use raphtory::{ - db::{ - api::view::Filter, - graph::views::filter::model::{EdgeViewFilterOps, ViewWrapOps}, - }, - prelude::*, - test_storage, - test_utils::test_graph, - }; - use raphtory_api::core::{ - entities::LayerId, - storage::{arc_str::ArcStr, timeindex::AsTime}, - }; - use std::collections::HashMap; - - #[test] - fn test_properties() { - let graph = Graph::new(); - let props = [(ArcStr::from("test"), "test".into_prop())]; - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 2, props.clone(), None).unwrap(); - test_storage!(&graph, |graph| { - let e1 = graph.edge(1, 2).unwrap(); - let e1_w = graph.window(0, 1).edge(1, 2).unwrap(); - assert_eq!( - HashMap::from_iter(e1.properties().as_vec()), - props.into_iter().collect::>() - ); - assert!(e1_w.properties().as_vec().is_empty()) - }); - } - - #[test] - fn test_metadata() { - let graph = Graph::new(); - graph - .add_edge(1, 1, 2, NO_PROPS, Some("layer 1")) - .unwrap() - .add_metadata([("test_prop", "test_val")], None) - .unwrap(); - graph - .add_edge(1, 2, 3, NO_PROPS, Some("layer 2")) - .unwrap() - .add_metadata([("test_prop", "test_val"), ("other", "2")], None) - .unwrap(); - - graph - .add_edge(1, 2, 3, NO_PROPS, Some("layer 3")) - .unwrap() - .add_metadata([("test_prop", "test_val"), ("other", "3")], None) - .unwrap(); - - // FIXME: #18 metadata prop for edges - test_graph(&graph, |graph| { - assert_eq!( - graph.edge(1, 2).unwrap().metadata().get("test_prop"), - Some(Prop::map([("layer 1", "test_val")])) - ); - assert_eq!( - graph.edge(2, 3).unwrap().metadata().get("test_prop"), - Some(Prop::map([ - ("layer 2", "test_val"), - ("layer 3", "test_val") - ])) - ); - - assert_eq!( - graph.edge(2, 3).unwrap().metadata().get("other"), - Some(Prop::map([("layer 2", "2"), ("layer 3", "3")])) - ); - - assert_eq!( - graph - .valid_layers(["layer 3", "layer 2"]) - .edge(2, 3) - .unwrap() - .metadata() - .get("other"), - Some(Prop::map([("layer 2", "2"), ("layer 3", "3")])) - ); - - for e in graph.edges() { - for ee in e.explode() { - assert_eq!(ee.metadata().get("test_prop"), Some("test_val".into())) - } - } - }); - } - - #[test] - fn test_property_additions() { - let graph = Graph::new(); - let props = [("test", "test")]; - let e1 = graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - e1.add_updates(2, props, None).unwrap(); // same layer works - assert!(e1.add_updates(2, props, Some("test2")).is_err()); // different layer is error - let e = graph.edge(1, 2).unwrap(); - e.add_updates(2, props, Some("test2")).unwrap(); // non-restricted edge view can create new layers - let layered_views = e.explode_layers().into_iter().collect_vec(); - for ev in layered_views { - let layer = ev.layer_name().unwrap(); - assert!(ev.add_updates(1, props, Some("test")).is_err()); // restricted edge view cannot create updates in different layer - ev.add_updates(1, [("test2", layer)], None).unwrap() // this will add an update to the same layer as the view (not the default layer) - } - - let e1_w = e1.window(0, 1); - assert_eq!( - e1.properties().as_map(), - props - .into_iter() - .map(|(k, v)| (ArcStr::from(k), v.into_prop())) - .chain([(ArcStr::from("test2"), "_default".into_prop())]) - .collect::>() - ); - assert_eq!( - e.layers("test2").unwrap().properties().as_map(), - props - .into_iter() - .map(|(k, v)| (ArcStr::from(k), v.into_prop())) - .chain([(ArcStr::from("test2"), "test2".into_prop())]) - .collect::>() - ); - assert_eq!(e1_w.properties().as_map(), HashMap::default()) - } - - #[test] - fn test_metadata_additions() { - let g = Graph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); - assert_eq!(e.edge.layer(), Some(LayerId(1))); // 0 is static graph - assert!(e.add_metadata([("test1", "test1")], None).is_ok()); // adds properties to layer `"test"` - assert!(e.add_metadata([("test", "test")], Some("test2")).is_err()); // cannot add properties to a different layer - e.add_metadata([("test", "test")], Some("test")).unwrap(); // layer is consistent - assert_eq!(e.metadata().get("test"), Some("test".into())); - assert_eq!(e.metadata().get("test1"), Some("test1".into())); - } - - #[test] - fn test_metadata_updates() { - let g = Graph::new(); - let e = g.add_edge(0, 1, 2, NO_PROPS, Some("test")).unwrap(); - assert!(e.add_metadata([("test1", "test1")], None).is_ok()); // adds properties to layer `"test"` - assert!(e.update_metadata([("test1", "test2")], None).is_ok()); - assert_eq!(e.metadata().get("test1"), Some("test2".into())); - } - - #[test] - fn test_layers_earliest_time() { - let g = Graph::new(); - let e = g.add_edge(1, 1, 2, NO_PROPS, Some("test")).unwrap(); - assert_eq!(e.earliest_time().map(|t| t.t()), Some(1)); - } - - #[test] - fn test_edge_layers() { - let graph = Graph::new(); - - graph - .add_edge(0, 1, 2, NO_PROPS, Some("fire_nation")) - .unwrap(); - graph - .add_edge(0, 2, 3, [("a", "test")], Some("fire_nation")) - .unwrap(); - graph - .add_edge(0, 3, 4, [("b", 4)], Some("air_nomads")) - .unwrap(); - - let filter_expr = EdgeFilter.layer("fire_nation").is_active(); - let ids = graph - .filter(filter_expr) - .unwrap() - .edges() - .iter() - .map(|n| n.src().name() + "->" + &n.dst().name()) - .collect::>(); - - assert_eq!(ids, ["1->2", "2->3"]); - - let filter_expr = EdgeFilter.layer("air_nomads").is_active(); - let ids = graph - .filter(filter_expr) - .unwrap() - .edges() - .iter() - .map(|n| n.src().name() + "->" + &n.dst().name()) - .collect::>(); - - assert_eq!(ids, ["3->4"]); - } -} diff --git a/raphtory/tests/test_edge_view.rs b/raphtory/tests/test_edge_view.rs deleted file mode 100644 index 8172a74b7a..0000000000 --- a/raphtory/tests/test_edge_view.rs +++ /dev/null @@ -1,164 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use raphtory::{prelude::*, test_storage, test_utils::test_graph}; - - #[test] - fn test_exploded_edge_properties() { - let graph = Graph::new(); - let actual_prop_values = vec![0, 1, 2, 3]; - for v in actual_prop_values.iter() { - graph.add_edge(0, 1, 2, [("test", *v)], None).unwrap(); - } - - test_storage!(&graph, |graph| { - let prop_values: Vec<_> = graph - .edge(1, 2) - .unwrap() - .explode() - .properties() - .flat_map(|p| p.get("test").into_i32()) - .collect(); - assert_eq!(prop_values, actual_prop_values) - }); - } - - #[test] - fn test_exploded_edge_properties_window() { - let graph = Graph::new(); - let actual_prop_values_0 = vec![0, 1, 2, 3]; - for v in actual_prop_values_0.iter() { - graph.add_edge(0, 1, 2, [("test", *v)], None).unwrap(); - } - let actual_prop_values_1 = vec![4, 5, 6]; - for v in actual_prop_values_1.iter() { - graph.add_edge(1, 1, 2, [("test", *v)], None).unwrap(); - } - test_storage!(&graph, |graph| { - let prop_values: Vec<_> = graph - .at(0) - .edge(1, 2) - .unwrap() - .explode() - .properties() - .flat_map(|p| p.get("test").into_i32()) - .collect(); - assert_eq!(prop_values, actual_prop_values_0); - let prop_values: Vec<_> = graph - .at(1) - .edge(1, 2) - .unwrap() - .explode() - .properties() - .flat_map(|p| p.get("test").into_i32()) - .collect(); - assert_eq!(prop_values, actual_prop_values_1) - }); - } - - #[test] - fn test_exploded_edge_multilayer() { - let graph = Graph::new(); - let expected_prop_values = vec![0, 1, 2, 3]; - for v in expected_prop_values.iter() { - graph - .add_edge(0, 1, 2, [("test", *v)], Some((v % 2).to_string().as_str())) - .unwrap(); - } - // FIXME: Needs support for event id from EventTime in disk storage (Issue #30) - test_graph(&graph, |graph| { - let prop_values: Vec<_> = graph - .edge(1, 2) - .unwrap() - .explode() - .properties() - .flat_map(|p| p.get("test").into_i32()) - .collect(); - let actual_layers: Vec<_> = graph - .edge(1, 2) - .unwrap() - .explode() - .layer_name() - .flatten() - .collect(); - let expected_layers: Vec<_> = expected_prop_values - .iter() - .map(|v| (v % 2).to_string()) - .collect(); - assert_eq!(prop_values, expected_prop_values); - assert_eq!(actual_layers, expected_layers); - - assert!(graph.edge(1, 2).unwrap().layer_name().is_err()); - assert!(graph.edges().layer_name().all(|l| l.is_err())); - assert!(graph - .edge(1, 2) - .unwrap() - .explode() - .layer_name() - .all(|l| l.is_ok())); - assert!(graph - .edge(1, 2) - .unwrap() - .explode_layers() - .layer_name() - .all(|l| l.is_ok())); - assert!(graph.edges().explode().layer_name().all(|l| l.is_ok())); - assert!(graph - .edges() - .explode_layers() - .layer_name() - .all(|l| l.is_ok())); - - assert!(graph.edge(1, 2).unwrap().time().is_err()); - assert!(graph.edges().time().all(|l| l.is_err())); - assert!(graph - .edge(1, 2) - .unwrap() - .explode() - .time() - .all(|l| l.is_ok())); - assert!(graph - .edge(1, 2) - .unwrap() - .explode_layers() - .time() - .all(|l| l.is_err())); - assert!(graph.edges().explode().time().all(|l| l.is_ok())); - assert!(graph.edges().explode_layers().time().all(|l| l.is_err())); - }); - } - - #[test] - fn test_sorting_by_event_id() { - let graph = Graph::new(); - graph.add_edge(0, 2, 3, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(0, 1, 2, [("second", true)], None).unwrap(); - graph.add_edge(0, 2, 3, [("second", true)], None).unwrap(); - - //FIXME: DiskGraph does not preserve event id (see #1780) - test_graph(&graph, |graph| { - let mut exploded_edges: Vec<_> = graph.edges().explode().into_iter().collect(); - exploded_edges.sort_by_key(|a| a.time_and_event_id().unwrap()); - - let res: Vec<_> = exploded_edges - .into_iter() - .filter_map(|e| { - Some(( - e.src().id().as_u64()?, - e.dst().id().as_u64()?, - e.properties().get("second").into_bool(), - )) - }) - .collect(); - assert_eq!( - res, - vec![ - (2, 3, None), - (1, 2, None), - (1, 2, Some(true)), - (2, 3, Some(true)) - ] - ) - }); - } -} diff --git a/raphtory/tests/test_exploded_edges.rs b/raphtory/tests/test_exploded_edges.rs deleted file mode 100644 index a79a5e49ef..0000000000 --- a/raphtory/tests/test_exploded_edges.rs +++ /dev/null @@ -1,553 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use raphtory::{prelude::*, test_storage}; - - #[test] - fn test_add_node_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph - .add_node((0, 3), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); - } - - #[test] - fn test_add_node_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph - .add_node((0, 1), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node(0) - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph - .add_node((0, 1), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node(0) - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); - } - - #[test] - fn test_create_node_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph - .create_node((0, 3), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); - } - - #[test] - fn test_create_node_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph - .create_node((0, 1), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 1), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node(0) - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph - .create_node((0, 1), 0, [("prop", "1")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "2")], None, None) - .unwrap(); - graph - .add_node((0, 2), 0, [("prop", "3")], None, None) - .unwrap(); - - let props = graph - .node(0) - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); - } - - #[test] - fn test_add_edge_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph.add_edge((0, 3), 0, 1, [("prop", "1")], None).unwrap(); - graph.add_edge((0, 2), 0, 1, [("prop", "2")], None).unwrap(); - graph.add_edge((0, 1), 0, 1, [("prop", "3")], None).unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); - } - - #[test] - fn test_add_edge_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph.add_edge((0, 1), 0, 1, [("prop", "1")], None).unwrap(); - graph.add_edge((0, 1), 0, 1, [("prop", "2")], None).unwrap(); - graph.add_edge((0, 1), 0, 1, [("prop", "3")], None).unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph.add_edge((0, 1), 0, 1, [("prop", "1")], None).unwrap(); - graph.add_edge((0, 2), 0, 1, [("prop", "2")], None).unwrap(); - graph.add_edge((0, 2), 0, 1, [("prop", "3")], None).unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); - } - - #[test] - fn test_add_properties_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph.add_properties((0, 3), [("prop", "1")]).unwrap(); - graph.add_properties((0, 2), [("prop", "2")]).unwrap(); - graph.add_properties((0, 1), [("prop", "3")]).unwrap(); - - let props = graph - .properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); - } - - #[test] - fn test_add_properties_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph.add_properties((0, 1), [("prop", "1")]).unwrap(); - graph.add_properties((0, 1), [("prop", "2")]).unwrap(); - graph.add_properties((0, 1), [("prop", "3")]).unwrap(); - - let props = graph - .properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph.add_edge((0, 1), 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge((0, 2), 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge((0, 2), 0, 1, NO_PROPS, None).unwrap(); - - graph.add_properties((0, 1), [("prop", "1")]).unwrap(); - graph.add_properties((0, 2), [("prop", "2")]).unwrap(); - graph.add_properties((0, 2), [("prop", "3")]).unwrap(); - - let props = graph - .properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); - } - - #[test] - fn test_node_add_updates_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - - graph - .node(0) - .unwrap() - .add_updates((0, 3), [("prop", "1")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 2), [("prop", "2")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "3")], None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); - } - - #[test] - fn test_node_add_updates_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "1")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "2")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "3")], None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - - graph - .node(0) - .unwrap() - .add_updates((0, 1), [("prop", "1")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 2), [("prop", "2")], None) - .unwrap(); - graph - .node(0) - .unwrap() - .add_updates((0, 2), [("prop", "3")], None) - .unwrap(); - - let props = graph - .node("0") - .map(|node| { - node.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); - } - - #[test] - fn test_edge_add_updates_properties_ordered_by_event_id() { - let graph: Graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 3), [("prop", "1")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 2), [("prop", "2")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "3")], None) - .unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!( - props, - vec!["3".to_string(), "2".to_string(), "1".to_string()] - ); - } - - #[test] - fn test_edge_add_updates_properties_overwritten_for_same_event_id() { - let graph: Graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "1")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "2")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "3")], None) - .unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["3".to_string()]); - - let graph: Graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 1), [("prop", "1")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 2), [("prop", "2")], None) - .unwrap(); - graph - .edge(0, 1) - .unwrap() - .add_updates((0, 2), [("prop", "3")], None) - .unwrap(); - - let props = graph - .edge(0, 1) - .map(|edge| { - edge.properties() - .temporal() - .get("prop") - .unwrap() - .values() - .map(|x| x.to_string()) - .collect_vec() - }) - .unwrap(); - - assert_eq!(props, vec!["1".to_string(), "3".to_string()]); - } - - #[test] - fn test_exploded_edges() { - let graph: Graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(1, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(2, 0, 1, NO_PROPS, None).unwrap(); - graph.add_edge(3, 0, 1, NO_PROPS, None).unwrap(); - test_storage!(&graph, |graph| { - assert_eq!(graph.count_temporal_edges(), 4) - }); - } -} diff --git a/raphtory/tests/test_filters.rs b/raphtory/tests/test_filters.rs deleted file mode 100644 index eb89af59a4..0000000000 --- a/raphtory/tests/test_filters.rs +++ /dev/null @@ -1,12355 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use raphtory::{ - db::{api::view::StaticGraphViewOps, graph::assertions::GraphTransformer}, - prelude::*, - }; - - #[cfg(test)] - mod test_composite_filters { - use raphtory::{ - db::graph::views::filter::model::{ - edge_filter::EdgeFilter, filter::Filter, node_filter::NodeFilter, - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, - prelude::IntoProp, - }; - use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; - - #[test] - fn test_fuzzy_search() { - let filter = Filter::fuzzy_search("name", "pomet", 2, false); - assert!(filter.matches(Some("pometry"))); - - let filter = Filter::fuzzy_search("name", "shivam_kapoor", 2, false); - assert!(filter.matches(Some("shivam_kapoor2"))); - - let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); - assert!(filter.matches(Some("shivam_kapoor2"))); - - let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); - assert!(filter.matches(Some("shivam_kapoor2"))); - - let filter = Filter::fuzzy_search("name", "shivam kapoor", 2, false); - assert!(!filter.matches(Some("shivam1_kapoor2"))); - - let filter = Filter::fuzzy_search("name", "khivam sapoor", 2, false); - assert!(!filter.matches(Some("shivam1_kapoor2"))); - } - - #[test] - fn test_fuzzy_search_prefix_match() { - let filter = Filter::fuzzy_search("name", "pome", 2, false); - assert!(!filter.matches(Some("pometry"))); - - let filter = Filter::fuzzy_search("name", "pome", 2, true); - assert!(filter.matches(Some("pometry"))); - } - - #[test] - fn test_fuzzy_search_property() { - let filter = NodeFilter.property("prop").fuzzy_search("pomet", 2, false); - assert!(filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); - } - - #[test] - fn test_fuzzy_search_property_prefix_match() { - let filter = EdgeFilter.property("prop").fuzzy_search("pome", 2, false); - assert!(!filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); - - let filter = EdgeFilter.property("prop").fuzzy_search("pome", 2, true); - assert!(filter.matches(Some(&Prop::Str(ArcStr::from("pometry"))))); - } - - #[test] - fn test_contains_match() { - let filter = EdgeFilter.property("prop").contains("shivam"); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); - assert!(res); - let res = filter.matches(None); - assert!(!res); - - let filter = EdgeFilter.property("prop").contains("am_ka"); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); - assert!(res); - } - - #[test] - fn test_contains_not_match() { - let filter = NodeFilter.property("prop").not_contains("shivam"); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam_kapoor")))); - assert!(!res); - let res = filter.matches(None); - assert!(!res); - } - - #[test] - fn test_is_in_match() { - let filter = NodeFilter - .property("prop") - .is_in(vec!["shivam".into_prop()]); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam")))); - assert!(res); - let res = filter.matches(None); - assert!(!res); - } - - #[test] - fn test_is_not_in_match() { - let filter = EdgeFilter - .property("prop") - .is_not_in(vec!["shivam".into_prop()]); - let res = filter.matches(Some(&Prop::Str(ArcStr::from("shivam")))); - assert!(!res); - let res = filter.matches(None); - assert!(!res); - } - } - - use raphtory_api::core::entities::properties::prop::IntoProp; - use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, - }; - - struct IdentityGraphTransformer; - - impl GraphTransformer for IdentityGraphTransformer { - type Return = G; - fn apply(&self, graph: G) -> Self::Return { - graph - } - } - - #[cfg(test)] - mod test_property_semantics { - #[cfg(test)] - mod test_node_property_filter_semantics { - use crate::test::IdentityGraphTransformer; - use raphtory::{ - db::{ - api::view::{filter_ops::Filter, StaticGraphViewOps}, - graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, TestVariants, - }, - views::filter::model::{ - node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, TemporalPropertyFilterFactory, - }, - }, - }, - errors::GraphError, - prelude::*, - }; - use raphtory_api::core::entities::properties::prop::Prop; - use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, - property_addition_ops::InternalPropertyAdditionOps, - }; - - fn init_graph( - graph: G, - ) -> G { - let nodes = [ - (6, "N1", vec![("p1", Prop::U64(2u64))]), - (7, "N1", vec![("p1", Prop::U64(1u64))]), - (6, "N2", vec![("p1", Prop::U64(1u64))]), - (7, "N2", vec![("p1", Prop::U64(2u64))]), - (8, "N3", vec![("p1", Prop::U64(1u64))]), - (9, "N4", vec![("p1", Prop::U64(1u64))]), - (5, "N5", vec![("p1", Prop::U64(1u64))]), - (6, "N5", vec![("p1", Prop::U64(2u64))]), - (5, "N6", vec![("p1", Prop::U64(1u64))]), - (6, "N6", vec![("p1", Prop::U64(1u64))]), - (3, "N7", vec![("p1", Prop::U64(1u64))]), - (5, "N7", vec![("p1", Prop::U64(1u64))]), - (3, "N8", vec![("p1", Prop::U64(1u64))]), - (4, "N8", vec![("p1", Prop::U64(2u64))]), - (2, "N9", vec![("p1", Prop::U64(2u64))]), - (2, "N10", vec![("q1", Prop::U64(0u64))]), - (2, "N10", vec![("p1", Prop::U64(3u64))]), - (2, "N11", vec![("p1", Prop::U64(3u64))]), - (2, "N11", vec![("q1", Prop::U64(0u64))]), - (2, "N12", vec![("q1", Prop::U64(0u64))]), - (3, "N12", vec![("p1", Prop::U64(3u64))]), - (2, "N13", vec![("q1", Prop::U64(0u64))]), - (3, "N13", vec![("p1", Prop::U64(3u64))]), - (2, "N14", vec![("q1", Prop::U64(0u64))]), - (2, "N15", vec![]), - ]; - - for (id, label, props) in nodes.iter() { - graph - .add_node(*id, label, props.clone(), None, None) - .unwrap(); - } - - let metadata = [ - ("N1", [("p1", Prop::U64(1u64))]), - ("N4", [("p1", Prop::U64(2u64))]), - ("N9", [("p1", Prop::U64(1u64))]), - ("N10", [("p1", Prop::U64(1u64))]), - ("N11", [("p1", Prop::U64(1u64))]), - ("N12", [("p1", Prop::U64(1u64))]), - ("N13", [("p1", Prop::U64(1u64))]), - ("N14", [("p1", Prop::U64(1u64))]), - ("N15", [("p1", Prop::U64(1u64))]), - ]; - - for (node, props) in metadata.iter() { - graph - .node(node) - .unwrap() - .add_metadata(props.clone()) - .unwrap(); - } - - graph - } - - fn init_graph_for_event_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let graph: G = init_graph(graph); - let nodes = [ - (1, "N16", vec![("p1", Prop::U64(2u64))]), - (1, "N16", vec![("p1", Prop::U64(1u64))]), - (1, "N17", vec![("p1", Prop::U64(1u64))]), - (1, "N17", vec![("p1", Prop::U64(2u64))]), - ]; - - for (id, label, props) in nodes.iter() { - graph - .add_node(*id, label, props.clone(), None, None) - .unwrap(); - } - - graph - } - - #[test] - fn test_metadata_semantics() { - let filter = NodeFilter.metadata("p1").eq(1u64); - let expected_results = vec!["N1", "N10", "N11", "N12", "N13", "N14", "N15", "N9"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_any_semantics() { - let filter = NodeFilter.property("p1").temporal().any().eq(1u64); - let expected_results = vec!["N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_any_semantics_for_event_ids() { - let filter = NodeFilter.property("p1").temporal().any().eq(1u64); - let expected_results = - vec!["N1", "N16", "N17", "N2", "N3", "N4", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_latest_semantics() { - let filter = NodeFilter.property("p1").temporal().last().eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_latest_semantics_for_event_ids() { - let filter = NodeFilter.property("p1").temporal().last().eq(1u64); - let expected_results = vec!["N1", "N16", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_property_semantics() { - // TODO: Const properties not supported for disk_graph. - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_property_semantics_for_event_ids() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N16", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_property_semantics_only_metadata() { - // For this graph there won't be any temporal property index for property name "p1". - fn init_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let nodes = [(2, "N1", vec![("q1", Prop::U64(0u64))]), (2, "N2", vec![])]; - - for (id, label, props) in nodes.iter() { - graph - .add_node(*id, label, props.clone(), None, None) - .unwrap(); - } - - let metadata = [ - ("N1", [("p1", Prop::U64(1u64))]), - ("N2", [("p1", Prop::U64(1u64))]), - ]; - - for (node, props) in metadata.iter() { - graph - .node(node) - .unwrap() - .add_metadata(props.clone()) - .unwrap(); - } - - graph - } - - let filter = NodeFilter.property("p1").ge(1u64); - let graph = init_graph(Graph::new()); - assert!(matches!( - graph.filter(filter.clone()).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "p1" - )); - assert!(matches!( - graph.persistent_graph().filter(filter).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "p1" - )); - } - - #[test] - fn test_property_semantics_only_temporal() { - // For this graph there won't be any metadata index for property name "p1". - fn init_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let nodes = [ - (1, "N1", vec![("p1", Prop::U64(1u64))]), - (2, "N2", vec![("p1", Prop::U64(1u64))]), - (3, "N2", vec![("p1", Prop::U64(2u64))]), - (2, "N3", vec![("p1", Prop::U64(2u64))]), - (3, "N3", vec![("p1", Prop::U64(1u64))]), - (3, "N4", vec![]), - ]; - - for (id, label, props) in nodes.iter() { - graph - .add_node(*id, label, props.clone(), None, None) - .unwrap(); - } - - graph - } - - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N1", "N3"]; - assert_filter_nodes_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - } - - #[cfg(test)] - mod test_edge_property_filter_semantics { - use crate::test::IdentityGraphTransformer; - use raphtory::{ - db::{ - api::view::{filter_ops::Filter, EdgeViewOps, StaticGraphViewOps}, - graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, - TestGraphVariants, TestVariants, WindowGraphTransformer, - }, - views::filter::{ - model::{ - edge_filter::EdgeFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, TemporalPropertyFilterFactory, - }, - CreateFilter, - }, - }, - }, - errors::GraphError, - prelude::*, - }; - use raphtory_api::core::entities::properties::prop::Prop; - use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, - property_addition_ops::InternalPropertyAdditionOps, - }; - - fn init_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let edges = [ - (6, "N1", "N2", vec![("p1", Prop::U64(2u64))]), - (7, "N1", "N2", vec![("p1", Prop::U64(1u64))]), - (6, "N2", "N3", vec![("p1", Prop::U64(1u64))]), - (7, "N2", "N3", vec![("p1", Prop::U64(2u64))]), - (8, "N3", "N4", vec![("p1", Prop::U64(1u64))]), - (9, "N4", "N5", vec![("p1", Prop::U64(1u64))]), - (5, "N5", "N6", vec![("p1", Prop::U64(1u64))]), - (6, "N5", "N6", vec![("p1", Prop::U64(2u64))]), - (5, "N6", "N7", vec![("p1", Prop::U64(1u64))]), - (6, "N6", "N7", vec![("p1", Prop::U64(1u64))]), - (3, "N7", "N8", vec![("p1", Prop::U64(1u64))]), - (5, "N7", "N8", vec![("p1", Prop::U64(1u64))]), - (3, "N8", "N9", vec![("p1", Prop::U64(1u64))]), - (4, "N8", "N9", vec![("p1", Prop::U64(2u64))]), - (2, "N9", "N10", vec![("p1", Prop::U64(2u64))]), - (2, "N10", "N11", vec![("q1", Prop::U64(0u64))]), - (2, "N10", "N11", vec![("p1", Prop::U64(3u64))]), - (2, "N11", "N12", vec![("p1", Prop::U64(3u64))]), - (2, "N11", "N12", vec![("q1", Prop::U64(0u64))]), - (2, "N12", "N13", vec![("q1", Prop::U64(0u64))]), - (3, "N12", "N13", vec![("p1", Prop::U64(3u64))]), - (2, "N13", "N14", vec![("q1", Prop::U64(0u64))]), - (3, "N13", "N14", vec![("p1", Prop::U64(3u64))]), - (2, "N14", "N15", vec![("q1", Prop::U64(0u64))]), - (2, "N15", "N1", vec![]), - ]; - - for (time, src, dst, props) in edges { - graph.add_edge(time, src, dst, props, None).unwrap(); - } - - let metadata_edges = [ - ("N1", "N2", vec![("p1", Prop::U64(1u64))]), - ("N4", "N5", vec![("p1", Prop::U64(2u64))]), - ("N9", "N10", vec![("p1", Prop::U64(1u64))]), - ("N10", "N11", vec![("p1", Prop::U64(1u64))]), - ("N11", "N12", vec![("p1", Prop::U64(1u64))]), - ("N12", "N13", vec![("p1", Prop::U64(1u64))]), - ("N13", "N14", vec![("p1", Prop::U64(1u64))]), - ("N14", "N15", vec![("p1", Prop::U64(1u64))]), - ("N15", "N1", vec![("p1", Prop::U64(1u64))]), - ]; - - for (src, dst, props) in metadata_edges { - graph - .edge(src, dst) - .unwrap() - .add_metadata(props.clone(), None) - .unwrap(); - } - - graph - } - - fn init_graph_for_event_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let graph: G = init_graph(graph); - let edge_data = [ - (1, "N16", "N15", vec![("p1", Prop::U64(2u64))]), - (1, "N16", "N15", vec![("p1", Prop::U64(1u64))]), - (1, "N17", "N16", vec![("p1", Prop::U64(1u64))]), - (1, "N17", "N16", vec![("p1", Prop::U64(2u64))]), - ]; - - for (time, src, dst, props) in edge_data { - graph.add_edge(time, src, dst, props, None).unwrap(); - } - - graph - } - - #[test] - fn test_persistent_graph_first_window() { - fn init_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - graph - .add_edge(0, 1, 2, [("p1", Prop::U64(1u64))], None) - .unwrap(); - graph - .add_edge(2, 1, 2, [("p1", Prop::U64(2u64))], None) - .unwrap(); - graph - .add_edge(5, 1, 2, [("p1", Prop::U64(5u64))], None) - .unwrap(); - graph - .add_edge(10, 1, 2, [("p1", Prop::U64(10u64))], None) - .unwrap(); - graph - } - - let filter = EdgeFilter.property("p1").temporal().first().eq(2u64); - - // No window; means the first update is at time 0 and the value of p1 is expected to be 1u64. - let expected_empty = []; - let expected_found = ["1->2"]; - - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_empty, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_empty, - TestVariants::PersistentOnly, - ); - - // Window(1,10); Expected emtpy because the first update is at time 0 and the value of p1 is expected to be 1u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(1..10), - filter.clone(), - &expected_empty, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(1..10), - filter.clone(), - &expected_empty, - TestVariants::PersistentOnly, - ); - - // Window(2,10); Expected update at time 2 and the value of p1 is expected to be 2u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(2..10), - filter.clone(), - &expected_found, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(2..10), - filter.clone(), - &expected_found, - TestVariants::PersistentOnly, - ); - - // Window(3,10); Expected update at time 2 (even if it is outside the window) and the value of p1 is expected to be 2u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(3..10), - filter.clone(), - &expected_found, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(3..10), - filter.clone(), - &expected_found, - TestVariants::PersistentOnly, - ); - - // Window(4,10); Expected update at time 2 (even if it is outside the window) and the value of p1 is expected to be 2u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(4..10), - filter.clone(), - &expected_found, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(4..10), - filter.clone(), - &expected_found, - TestVariants::PersistentOnly, - ); - - // Window(5,10); Expected update at time 5 (even if it is outside the window) and the value of p1 is expected to be 5u64. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(5..10), - filter.clone(), - &expected_empty, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(5..10), - filter.clone(), - &expected_empty, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_metadata_semantics() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.metadata("p1").eq(1u64); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", - "N15->N1", "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_metadata_semantics2() { - fn filter_edges(graph: &Graph, filter: impl CreateFilter) -> Vec { - let mut results = graph - .filter(filter) - .unwrap() - .edges() - .iter() - .map(|e| format!("{}->{}", e.src().name(), e.dst().name())) - .collect::>(); - results.sort(); - results - } - - let graph = init_graph(Graph::new()); - - let filter = EdgeFilter.metadata("p1").eq(1u64); - assert_eq!( - filter_edges(&graph, filter.clone()), - vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", - "N15->N1", "N9->N10" - ] - ); - - let edge = graph - .add_edge(1, "shivam", "kapoor", [("p1", 100u64)], Some("fire_nation")) - .unwrap(); - edge.add_metadata([("z", true)], Some("fire_nation")) - .unwrap(); - let prop = graph.edge("shivam", "kapoor").unwrap().metadata().get("z"); - assert_eq!(prop, Some(Prop::map([("fire_nation", true)]))); - - let filter2 = EdgeFilter - .metadata("z") - .eq(Prop::map([("fire_nation", true)])); - assert_eq!(filter_edges(&graph, filter2), vec!["shivam->kapoor"]); - - let filter = EdgeFilter - .metadata("p1") - .eq(Prop::map([("_default", 1u64)])); - assert_eq!( - filter_edges(&graph, filter), - vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", - "N15->N1", "N9->N10" - ] - ); - } - - #[test] - fn test_temporal_any_semantics() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").temporal().any().eq(1u64); - let expected_results = vec![ - "N1->N2", "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N9", - ]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_any_semantics_for_event_ids() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").temporal().any().lt(2u64); - let expected_results = vec![ - "N1->N2", "N16->N15", "N17->N16", "N2->N3", "N3->N4", "N4->N5", "N5->N6", - "N6->N7", "N7->N8", "N8->N9", - ]; - assert_filter_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_latest_semantics() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").temporal().last().eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_temporal_latest_semantics_for_event_ids() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").temporal().last().eq(1u64); - let expected_results = - vec!["N1->N2", "N16->N15", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_property_semantics() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p1").ge(2u64); - let expected_results = vec![ - "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", - "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_property_semantics_for_event_ids() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = - vec!["N1->N2", "N16->N15", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_graph_for_event_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_property_semantics_only_metadata() { - // For this graph there won't be any temporal property index for property name "p1". - fn init_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let edges = [ - (2, "N1", "N2", vec![("q1", Prop::U64(0u64))]), - (2, "N2", "N3", vec![]), - ]; - - for (time, src, dst, props) in edges { - graph.add_edge(time, src, dst, props, None).unwrap(); - } - - let metadata_edges = [ - ("N1", "N2", vec![("p1", Prop::U64(1u64))]), - ("N2", "N3", vec![("p1", Prop::U64(1u64))]), - ]; - - for (src, dst, props) in metadata_edges { - graph - .edge(src, dst) - .unwrap() - .add_metadata(props.clone(), None) - .unwrap(); - } - - graph - } - - let filter = EdgeFilter.property("p1").eq(1u64); - let graph = init_graph(Graph::new()); - assert!(matches!( - graph.filter(filter.clone()).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "p1" - )); - assert!(matches!( - graph.persistent_graph().filter(filter).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "p1" - )); - } - - #[test] - fn test_property_semantics_only_temporal() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - // For this graph there won't be any metadata index for property name "p1". - fn init_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let edges = [ - (1, "N1", "N2", vec![("p1", Prop::U64(1u64))]), - (2, "N2", "N3", vec![("p1", Prop::U64(1u64))]), - (3, "N2", "N3", vec![("p1", Prop::U64(2u64))]), - (2, "N3", "N4", vec![("p1", Prop::U64(2u64))]), - (3, "N3", "N4", vec![("p1", Prop::U64(1u64))]), - (2, "N4", "N5", vec![]), - ]; - - for (time, src, dst, props) in edges { - graph.add_edge(time, src, dst, props, None).unwrap(); - } - - graph - } - - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4"]; - assert_filter_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - } - } - - fn init_nodes_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let nodes = [ - ( - 1, - "1", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 5u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "2", - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_boat".into_prop()), - ("p40", 10u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "2", - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 15u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 4, - "2", - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 20u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "1", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 10u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 3, - "3", - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - Some("fire_nation"), - ), - ( - 4, - "1", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 15u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 3, - "4", - vec![ - ("p4", "pometry".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - None, - ), - ( - 4, - "4", - vec![ - ("p5", 12u64.into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_ship".into_prop()), - ], - None, - ), - ]; - - for (time, id, props, node_type) in nodes { - graph.add_node(time, id, props, node_type, None).unwrap(); - } - - let metadata = [ - ( - "1", - vec![ - ("m1", "pometry".into_prop()), - ("m2", "raphtory".into_prop()), - ], - ), - ("2", vec![("m1", "raphtory".into_prop())]), - ( - "3", - vec![ - ("m2", "pometry".into_prop()), - ("m3", "raphtory".into_prop()), - ], - ), - ( - "4", - vec![ - ("m3", "pometry".into_prop()), - ("m4", "raphtory".into_prop()), - ], - ), - ]; - - for (node_id, md) in metadata { - graph.node(node_id).unwrap().add_metadata(md).unwrap(); - } - - graph - } - - fn init_nodes_graph_with_num_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let nodes = [ - ( - 1, - 1, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 5u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - 2, - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_boat".into_prop()), - ("p40", 10u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - 2, - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 15u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 4, - 2, - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 20u64.into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - 1, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 10u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 3, - 3, - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - Some("fire_nation"), - ), - ( - 4, - 1, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p9", 5u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ("p40", 15u64.into_prop()), - ], - Some("fire_nation"), - ), - ( - 3, - 4, - vec![ - ("p4", "pometry".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - None, - ), - ( - 4, - 4, - vec![ - ("p5", 12u64.into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_ship".into_prop()), - ], - None, - ), - ]; - - for (time, id, props, node_type) in nodes { - graph.add_node(time, id, props, node_type, None).unwrap(); - } - - graph - } - - fn init_nodes_graph_with_str_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let nodes = [ - (1, "London", Some("fire_nation")), - (2, "Two", Some("air_nomads")), - (3, "Two", Some("air_nomads")), - (4, "Two", Some("air_nomads")), - (3, "London", Some("fire_nation")), - (3, "Tokyo", Some("fire_nation")), - (4, "London", Some("fire_nation")), - (3, "France Paris", None), - (4, "France Paris", None), - ]; - - for (time, id, node_type) in nodes { - graph.add_node(time, id, NO_PROPS, node_type, None).unwrap(); - } - - graph - } - - fn init_edges_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let edges = [ - ( - 1, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 4u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "2", - "3", - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "2", - "3", - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "3", - "1", - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - Some("fire_nation"), - ), - ( - 3, - "2", - "1", - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - None, - ), - ( - 4, - "David Gilmour", - "John Mayer", - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - None, - ), - ( - 4, - "John Mayer", - "Jimmy Page", - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - None, - ), - ]; - - for (time, src, dst, props, edge_type) in edges { - graph.add_edge(time, src, dst, props, edge_type).unwrap(); - } - - graph - } - - fn init_edges_graph2< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let edges = [ - ( - 1, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 6u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 7u64.into_prop()), - ("p10", "Gold_ship".into_prop()), - ("p20", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - "1", - "2", - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 4u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ], - Some("air_nomads"), - ), - ( - 2, - "2", - "3", - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "2", - "3", - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - "3", - "1", - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - Some("air_nomads"), - ), - ( - 3, - "2", - "1", - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - None, - ), - ]; - - for (time, src, dst, props, edge_type) in edges { - graph.add_edge(time, src, dst, props, edge_type).unwrap(); - } - - graph - } - - fn init_edges_graph_with_num_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let edges = [ - ( - 1, - 1, - 2, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p10", "Paper_airplane".into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - 1, - 2, - vec![ - ("p1", "shivam_kapoor".into_prop()), - ("p2", 4u64.into_prop()), - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_ship".into_prop()), - ], - Some("fire_nation"), - ), - ( - 2, - 2, - 3, - vec![ - ("p1", "prop12".into_prop()), - ("p2", 2u64.into_prop()), - ("p10", "Paper_ship".into_prop()), - ("p20", "Gold_boat".into_prop()), - ("p30", "Old_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - 2, - 3, - vec![ - ("p20", "Gold_ship".into_prop()), - ("p30", "Gold_boat".into_prop()), - ], - Some("air_nomads"), - ), - ( - 3, - 3, - 1, - vec![("p2", 6u64.into_prop()), ("p3", 1u64.into_prop())], - Some("fire_nation"), - ), - ( - 3, - 2, - 1, - vec![ - ("p2", 6u64.into_prop()), - ("p3", 1u64.into_prop()), - ("p10", "Paper_airplane".into_prop()), - ], - None, - ), - ]; - - for (time, src, dst, props, edge_type) in edges { - graph.add_edge(time, src, dst, props, edge_type).unwrap(); - } - - graph - } - - fn init_edges_graph_with_str_ids< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let edges = [ - (1, "London", "Paris", Some("fire_nation")), - (2, "London", "Paris", Some("fire_nation")), - (2, "Two", "Three", Some("air_nomads")), - (3, "Two", "Three", Some("air_nomads")), - (3, "Three", "One", Some("fire_nation")), - (3, "Two", "One", None), - (4, "David Gilmour", "John Mayer", None), - (4, "John Mayer", "Jimmy Page", None), - ]; - - for (time, src, dst, edge_type) in edges { - graph.add_edge(time, src, dst, NO_PROPS, edge_type).unwrap(); - } - - graph - } - - fn init_edges_graph_with_str_ids_del< - G: StaticGraphViewOps - + AdditionOps - + DeletionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let edges = [ - (1, "London", "Paris", Some("fire_nation")), - (2, "London", "Paris", Some("fire_nation")), - (2, "Two", "Three", Some("air_nomads")), - (3, "Two", "Three", Some("air_nomads")), - (3, "Three", "One", Some("fire_nation")), - (3, "Two", "One", None), - (4, "David Gilmour", "John Mayer", None), - (4, "John Mayer", "Jimmy Page", None), - ]; - - for (time, src, dst, edge_type) in edges { - graph.add_edge(time, src, dst, NO_PROPS, edge_type).unwrap(); - } - - graph - .delete_edge(3, "London", "Paris", Some("fire_nation")) - .unwrap(); - - graph - .add_edge(5, "Bangalore", "Bangalore", NO_PROPS, None) - .unwrap(); - - graph - } - - mod test_node_filter { - use crate::test::{ - init_nodes_graph, init_nodes_graph_with_num_ids, init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - }; - use raphtory::{ - algorithms::alternating_mask::alternating_mask, - db::{ - api::view::{filter_ops::NodeSelect, Filter}, - graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - assert_select_nodes_results, TestVariants, - }, - views::filter::model::{ - node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, - ComposableFilter, CompositeNodeFilter, NodeViewFilterOps, - TryAsCompositeFilter, ViewWrapOps, - }, - }, - }, - prelude::{ - AdditionOps, Graph, GraphViewOps, NodeFilter, NodeStateOps, NodeViewOps, TimeOps, - NO_PROPS, - }, - }; - - #[test] - fn test_node_list_is_preserved() { - let graph = init_nodes_graph(Graph::new()); - let nodes = graph - .nodes() - .after(5) - .select(NodeFilter::node_type().contains("x")) - .unwrap(); - let degrees = nodes.degree(); - let degrees_collected = degrees.compute(); - assert_eq!(degrees, degrees_collected); - } - - #[test] - fn test_filter_nodes_for_node_name_eq() { - let filter = NodeFilter::name().eq("3"); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_name_ne() { - let filter = NodeFilter::name().ne("2"); - let expected_results = vec!["1", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_name_in() { - let filter = NodeFilter::name().is_in(vec!["1"]); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name().is_in(vec![""]); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name().is_in(vec!["2", "3"]); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_name_not_in() { - let filter = NodeFilter::name().is_not_in(vec!["1"]); - let expected_results = vec!["2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name().is_not_in(vec![""]); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_type_eq() { - let filter = NodeFilter::node_type().eq("fire_nation"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_type_ne() { - let filter = NodeFilter::node_type().ne("fire_nation"); - let expected_results = vec!["2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_type_in() { - let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomads"]); - let expected_results = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_type_not_in() { - let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); - let expected_results = vec!["2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_type_starts_with() { - let filter = NodeFilter::node_type().starts_with("fire"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().starts_with("rocket"); - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_type_ends_with() { - let filter = NodeFilter::node_type().ends_with("nomads"); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().ends_with("circle"); - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_type_contains() { - let filter = NodeFilter::node_type().contains("fire"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_node_type_contains_not() { - let filter = NodeFilter::node_type().not_contains("fire"); - let expected_results = vec!["2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_fuzzy_search() { - let filter = NodeFilter::node_type().fuzzy_search("fire", 2, true); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().fuzzy_search("fire", 2, false); - let expected_results: Vec<&str> = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type().fuzzy_search("air_noma", 2, false); - let expected_results: Vec<&str> = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_not_node_type() { - let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]).not(); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_eq_node_id() { - let filter = NodeFilter::id().eq("1"); - let expected_results = vec!["1"]; - - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::id().eq(1); - let expected_results = vec!["1"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_ne_node_id() { - let filter = NodeFilter::id().ne("1"); - let expected_results = vec!["2", "3", "4"]; - - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::id().ne(1); - let expected_results = vec!["2", "3", "4"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_is_in_node_id() { - let filter = NodeFilter::id().is_in(vec!["1", "3", "6"]); - let expected_results = vec!["1", "3"]; - - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::id().is_in(vec![1, 3, 6]); - let expected_results = vec!["1", "3"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_is_not_in_node_id() { - let filter = NodeFilter::id().is_not_in(vec!["1", "3", "6"]); - let expected_results = vec!["2", "4"]; - - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::id().is_not_in(vec![1, 3, 6]); - let expected_results = vec!["2", "4"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_lt_node_id() { - let filter = NodeFilter::id().lt(2); - let expected_results = vec!["1"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_le_node_id() { - let filter = NodeFilter::id().le(3); - let expected_results = vec!["1", "2", "3"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_gt_node_id() { - let filter = NodeFilter::id().gt(2); - let expected_results = vec!["3", "4"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_ge_node_id() { - let filter = NodeFilter::id().ge(2); - let expected_results = vec!["2", "3", "4"]; - - assert_filter_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_starts_with_node_id() { - let filter = NodeFilter::id().starts_with("France"); - let expected_results = vec!["France Paris"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_ends_with_node_id() { - let filter = NodeFilter::id().ends_with("wo"); - let expected_results = vec!["Two"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_contains_node_id() { - let filter = NodeFilter::id().contains("o"); - let expected_results = vec!["London", "Tokyo", "Two"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_not_contains_node_id() { - let filter = NodeFilter::id().not_contains("o"); - let expected_results = vec!["France Paris"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_is_in_node_id_str() { - let filter = NodeFilter::id().is_in(vec!["London", "Tokyo"]); - let expected_results = vec!["London", "Tokyo"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_is_not_in_node_id_str() { - let filter = NodeFilter::id().is_not_in(vec!["London", "Tokyo"]); - let expected_results = vec!["France Paris", "Two"]; - assert_filter_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_is_active_node_window() { - let filter = NodeFilter.window(1, 10).is_active(); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_is_active_node_window_not() { - let filter = NodeFilter - .window(1, 10) - .is_active() - .try_as_composite_node_filter() - .unwrap(); - let filter = CompositeNodeFilter::Not(Box::new(filter)); - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_is_active_node_latest() { - let filter = NodeFilter.latest().is_active(); - let expected_results = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_filter_by_column() { - let graph = Graph::new(); - graph.add_node(1, 1, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 2, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 3, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 4, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 5, NO_PROPS, None, None).unwrap(); - - let mask = alternating_mask(&graph); - let expected_nodes: Vec<_> = graph - .nodes() - .name() - .iter_values() - .skip(1) - .step_by(2) - .collect(); - - let filtered = graph - .filter(NodeFilter::by_column(&mask, "bool_col").unwrap()) - .unwrap(); - - let names = filtered - .nodes() - .iter() - .map(|n| n.id().to_string()) - .collect::>(); - - assert_eq!(names, expected_nodes); - - let filtered = graph - .nodes() - .select(NodeFilter::by_column(&mask, "bool_col").unwrap()) - .unwrap(); - - let names = filtered - .iter() - .map(|n| n.id().to_string()) - .collect::>(); - - assert_eq!(names, expected_nodes); - } - - #[test] - fn test_is_active_node_snapshot_at() { - let filter = NodeFilter.snapshot_at(2).is_active(); - let expected_results = vec!["2"]; - assert_select_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - - #[cfg(test)] - mod test_node_property_filter { - use crate::test::{init_nodes_graph, IdentityGraphTransformer}; - use raphtory::db::graph::{ - assertions::{assert_filter_nodes_results, assert_search_nodes_results, TestVariants}, - views::filter::model::{ - graph_filter::GraphFilter, - node_filter::NodeFilter, - not_filter::NotFilter, - property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, - windowed_filter::Windowed, - ComposableFilter, PropertyFilterFactory, TemporalPropertyFilterFactory, - ViewWrapOps, - }, - }; - use raphtory_api::core::entities::properties::prop::Prop; - use std::vec; - - #[test] - fn test_exact_match() { - // let filter = NodeFilter.degree > 5 - let filter = NodeFilter.property("p10").eq("Paper_airplane"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p10").eq(""); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_not_exact_match() { - let filter = NodeFilter.property("p10").eq("Paper"); - let expected_results: Vec<&str> = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_eq() { - let filter = NodeFilter.property("p2").eq(2u64); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p30").temporal().first().eq("Old_boat"); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p20").temporal().all().eq("Gold_ship"); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_ne() { - let filter = NodeFilter.property("p2").ne(2u64); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p30").temporal().first().ne("Old_boat"); - let expected_results = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p1").temporal().all().ne("Gold_ship"); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_lt() { - let filter = NodeFilter.property("p2").lt(10u64); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").temporal().first().lt(10u64); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p9").temporal().all().lt(10u64); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_le() { - let filter = NodeFilter.property("p2").le(6u64); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p9").temporal().first().le(10u64); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p2").temporal().all().le(10u64); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_gt() { - let filter = NodeFilter.property("p2").gt(2u64); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").temporal().first().gt(5u64); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p9").temporal().all().gt(1u64); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_ge() { - let filter = NodeFilter.property("p2").ge(2u64); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").temporal().first().ge(5u64); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").temporal().all().ge(5u64); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_in() { - let filter = NodeFilter.property("p2").is_in(vec![Prop::U64(6)]); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .is_in(vec![Prop::U64(2), Prop::U64(6)]); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p40") - .temporal() - .first() - .is_in(vec![Prop::U64(5)]); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .temporal() - .any() - .is_in(vec![Prop::U64(2)]); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_not_in() { - let filter = NodeFilter.property("p2").is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .temporal() - .all() - .is_not_in(vec![Prop::U64(2)]); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_is_some() { - let filter = NodeFilter.property("p2").is_some(); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").is_some(); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_is_none() { - let filter = NodeFilter.property("p2").is_none(); - let expected_results = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p40").is_none(); - let expected_results = vec!["3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_starts_with() { - let filter = NodeFilter.property("p10").starts_with("Pa"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .any() - .starts_with("Pap"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .starts_with("Pape"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .starts_with("Yohan"); - let expected_results: Vec<&str> = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p30") - .temporal() - .first() - .starts_with("Gold"); - let expected_results: Vec<&str> = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p20") - .temporal() - .all() - .starts_with("Gold"); - let expected_results: Vec<&str> = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_ends_with() { - let filter = NodeFilter.property("p10").ends_with("lane"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .any() - .ends_with("ship"); - let expected_results: Vec<&str> = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .ends_with("ane"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .ends_with("Jerry"); - let expected_results: Vec<&str> = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p20") - .temporal() - .first() - .ends_with("boat"); - let expected_results: Vec<&str> = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p20") - .temporal() - .all() - .ends_with("ship"); - let expected_results: Vec<&str> = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_contains() { - let filter = NodeFilter.property("p10").contains("Paper"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .any() - .contains("Paper"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .contains("Paper"); - let expected_results: Vec<&str> = vec!["1", "2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p30") - .temporal() - .first() - .contains("Old"); - let expected_results: Vec<&str> = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p30").temporal().all().contains("Gold"); - let expected_results: Vec<&str> = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_property_contains_not() { - let filter = NodeFilter.property("p10").not_contains("ship"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .any() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p10") - .temporal() - .last() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p30") - .temporal() - .first() - .not_contains("Old"); - let expected_results: Vec<&str> = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p30") - .temporal() - .all() - .not_contains("boat"); - let expected_results: Vec<&str> = vec!["1", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_not_property() { - let filter = NotFilter(NodeFilter.property("p10").contains("Paper")); - let expected_results: Vec<&str> = vec!["4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p10").contains("Paper").not(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_temporal_property_sum() { - let filter = NodeFilter.property("p9").temporal().sum().eq(15u64); - let expected_results: Vec<&str> = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_temporal_property_avg() { - let filter = NodeFilter.property("p2").temporal().avg().le(10f64); - let expected_results: Vec<&str> = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_temporal_property_min() { - let filter = NodeFilter.property("p40").temporal().min().is_in(vec![ - Prop::U64(5), - Prop::U64(10), - Prop::U64(20), - ]); - let expected_results: Vec<&str> = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_temporal_property_max() { - let filter = NodeFilter.property("p3").temporal().max().is_not_in(vec![ - Prop::U64(5), - Prop::U64(10), - Prop::U64(20), - ]); - let expected_results: Vec<&str> = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_nodes_for_temporal_property_len() { - let filter = NodeFilter.property("p2").temporal().len().le(5u64); - let expected_results: Vec<&str> = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_window_filter() { - let filter = NodeFilter - .window(1, 3) - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - // Wider window includes node 3 - let filter = NodeFilter - .window(1, 5) - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_window_filter_on_non_temporal_property() { - let filter1 = NodeFilter.window(1, 2).property("p1").eq("shivam_kapoor"); - let filter2 = NodeFilter - .window(100, 200) - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter1.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter1.clone(), - &expected_results, - TestVariants::All, - ); - - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = NodeFilter - .window(100, 200) - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_window_filter_any_all_over_window() { - let filter = NodeFilter - .window(3, 5) - .property("p20") - .temporal() - .any() - .eq("Gold_boat"); - - let expected_results = vec!["4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .window(3, 5) - .property("p20") - .temporal() - .all() - .eq("Gold_boat"); - - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_window_filter_and() { - // Filters both node 1 and 3 - let filter1 = NodeFilter - .window(1, 4) - .property("p10") - .temporal() - .any() - .eq("Paper_airplane"); - - // Filters only node 3 - let filter2 = NodeFilter - .window(3, 6) - .property("p2") - .temporal() - .sum() - .eq(6u64); - - let filter = filter1.and(filter2); - - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_at_filter() { - // Only time=2 contributes; node 2 has p2=2 at t=2 - let filter = NodeFilter.at(2).property("p2").temporal().sum().eq(2u64); - - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - // Only time=3 contributes; node 3 has p2=6 at t=3 - let filter = NodeFilter.at(3).property("p2").temporal().sum().eq(6u64); - - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_after_filter() { - // after(2) means t >= 3 - let filter = NodeFilter.after(2).property("p2").temporal().sum().ge(6u64); - - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_before_filter() { - // before(3) means t <= 2 - let filter = NodeFilter - .before(3) - .property("p2") - .temporal() - .sum() - .eq(2u64); - - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - // And node 3 shouldn't match, because its p2=6 lives at t=3. - let filter = NodeFilter - .before(3) - .property("p2") - .temporal() - .sum() - .eq(6u64); - - let expected_results = vec![]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_latest_filter() { - // At latest time (currently t=4), only node 4 has p5=12 - let filter = NodeFilter.latest().property("p5").eq(12u64); - - let expected_results = vec!["4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_snapshot_at_semantics_event_graph() { - let t = 2; - - let filter_snapshot = NodeFilter.snapshot_at(t).property("p2").eq(2u64); - - let filter_before = NodeFilter.before(t + 1).property("p2").eq(2u64); - - let expected_results = vec!["2"]; - - // snapshot_at - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot, - &expected_results, - TestVariants::EventOnly, - ); - - // before(t+1) - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_before.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_before, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_snapshot_at_semantics_persistent_graph() { - let t = 2; - - let filter_snapshot = NodeFilter.snapshot_at(t).property("p2").eq(2u64); - - let filter_at = NodeFilter.at(t).property("p2").eq(2u64); - - let expected_results = vec!["2"]; - - // snapshot_at - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot, - &expected_results, - TestVariants::PersistentOnly, - ); - - // at(t) - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_at.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_at, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_snapshot_latest_semantics_event_graph() { - let filter_snapshot_latest = NodeFilter - .snapshot_latest() - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let filter_noop = NodeFilter.property("p2").temporal().sum().ge(2u64); - - // From your earlier window test, "2" and "3" are the ones with p2 values across time. - // If your underlying dataset changes, adjust this accordingly. - let expected_results = vec!["2", "3"]; - - // snapshot_latest - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot_latest.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot_latest, - &expected_results, - TestVariants::EventOnly, - ); - - // no-op baseline - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_noop.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_noop, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_snapshot_latest_semantics_persistent_graph() { - let filter_snapshot_latest = NodeFilter - .snapshot_latest() - .property("p1") - .eq("shivam_kapoor"); - - let filter_latest = NodeFilter.latest().property("p1").eq("shivam_kapoor"); - - let expected_results = vec!["1"]; - - // snapshot_latest - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot_latest.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_snapshot_latest, - &expected_results, - TestVariants::PersistentOnly, - ); - - // latest - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_latest.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter_latest, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_nodes_layer_filter() { - let filter = NodeFilter - .layer("_default") - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_nodes_layer_then_window_ordering() { - // In layer "fire_nation" within window [1,4), node "1" matches p1 == "shivam_kapoor". - let filter = NodeFilter - .layer("fire_nation") - .window(1, 4) - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1"]; - - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_nodes_window_then_layer_ordering() { - // Same semantics as above, but reversed chaining order. - let filter = NodeFilter - .window(1, 4) - .layer("fire_nation") - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1"]; - - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_graph_filter_window() { - let filter: Windowed = GraphFilter.window(1, 2); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.window(1, 3); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.window(4, 6); - let expected_results = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = GraphFilter.window(4, 6); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_graph_filter_layer() { - let filter = GraphFilter.layer("fire_nation"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.layer("air_nomads"); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_graph_filter_window_then_layer() { - let filter = GraphFilter.window(1, 3).layer("fire_nation"); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.window(4, 4).layer("air_nomads"); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - #[ignore] // TODO: Enable this when node layer is supported - fn test_graph_filter_layer_then_window() { - let filter = GraphFilter.layer("fire_nation").window(1, 3); - let expected_results = vec!["1", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_graph_filter_at() { - let filter = GraphFilter.at(1); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.at(2); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = GraphFilter.at(2); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = GraphFilter.at(3); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_graph_filter_after() { - let filter = GraphFilter.after(3); - let expected_results = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = GraphFilter.after(3); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_graph_filter_before() { - let filter = GraphFilter.before(2); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.before(3); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_graph_filter_snapshot_at() { - let filter = GraphFilter.snapshot_at(1); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.snapshot_at(3); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = GraphFilter.snapshot_at(4); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_graph_filter_snapshot_latest() { - let filter = GraphFilter.snapshot_latest(); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_graph_filter_latest() { - let filter = GraphFilter.latest(); - let expected_results = vec!["1", "2", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = GraphFilter.latest(); - let expected_results = vec!["1", "2", "3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - - #[cfg(test)] - mod test_node_composite_filter { - use raphtory_api::core::Direction; - - use crate::test::{init_edges_graph, init_nodes_graph, IdentityGraphTransformer}; - use raphtory::{ - db::graph::{ - assertions::{ - assert_filter_neighbours_results, assert_filter_nodes_results, - assert_search_nodes_results, TestVariants, - }, - views::filter::model::{ - node_filter::ops::NodeFilterOps, property_filter::ops::PropertyFilterOps, - ComposableFilter, PropertyFilterFactory, TryAsCompositeFilter, - }, - }, - prelude::NodeFilter, - }; - - #[test] - fn test_filter_nodes_by_props_added_at_different_times() { - let filter = NodeFilter - .property("p4") - .eq("pometry") - .and(NodeFilter.property("p5").eq(12u64)); - let expected_results = vec!["4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_unique_results_from_composite_filters() { - let filter = NodeFilter - .property("p2") - .ge(2u64) - .and(NodeFilter.property("p2").ge(1u64)); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .ge(2u64) - .or(NodeFilter.property("p2").ge(5u64)); - let expected_results = vec!["2", "3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_composite_filter_nodes() { - let filter = NodeFilter - .property("p2") - .eq(2u64) - .and(NodeFilter.property("p1").eq("kapoor")); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p2") - .eq(2u64) - .or(NodeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter.property("p1").eq("pometry").or(NodeFilter - .property("p2") - .eq(6u64) - .and(NodeFilter.property("p3").eq(1u64))); - let expected_results = vec!["3"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type() - .eq("fire_nation") - .and(NodeFilter.property("p1").eq("prop1")); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter - .property("p9") - .eq(5u64) - .and(NodeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::node_type() - .eq("fire_nation") - .and(NodeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name() - .eq("2") - .and(NodeFilter.property("p2").eq(2u64)); - let expected_results = vec!["2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name() - .eq("2") - .and(NodeFilter.property("p2").eq(2u64)) - .or(NodeFilter.property("p9").eq(5u64)); - let expected_results = vec!["1", "2"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_node_filter().unwrap(); - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_not_composite_filter_nodes() { - let filter = NodeFilter::name() - .eq("2") - .and(NodeFilter.property("p2").eq(2u64)) - .or(NodeFilter.property("p9").eq(5u64)) - .not(); - let expected_results = vec!["3", "4"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = NodeFilter::name() - .eq("2") - .not() - .and(NodeFilter.property("p2").eq(2u64)) - .or(NodeFilter.property("p9").eq(5u64)); - let expected_results = vec!["1"]; - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_out_neighbours_filter() { - let filter = NodeFilter::name() - .eq("2") - .and(NodeFilter.property("p2").eq(2u64)); - let expected_results = vec!["2"]; - assert_filter_neighbours_results( - |graph| init_edges_graph(init_nodes_graph(graph)), - IdentityGraphTransformer, - "1", - Direction::OUT, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_in_neighbours_filter() { - let filter = NodeFilter.property("p9").ge(1u64); - let expected_results = vec!["1"]; - assert_filter_neighbours_results( - |graph| init_edges_graph(init_nodes_graph(graph)), - IdentityGraphTransformer, - "2", - Direction::IN, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_neighbours_filter() { - let filter = NodeFilter.property("p10").contains("Paper"); - let expected_results = vec!["1", "3"]; - assert_filter_neighbours_results( - |graph| init_edges_graph(init_nodes_graph(graph)), - IdentityGraphTransformer, - "2", - Direction::BOTH, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - } - - #[cfg(test)] - mod test_node_property_filter_agg { - use crate::test::IdentityGraphTransformer; - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::{ - assert_filter_nodes_err, assert_filter_nodes_results, - assert_search_nodes_results, TestVariants::All, - }, - views::filter::{ - model::{ - node_filter::NodeFilter, - property_filter::ops::{ - ElemQualifierOps, ListAggOps, PropertyFilterOps, - }, - PropertyFilterFactory, TemporalPropertyFilterFactory, - TryAsCompositeFilter, - }, - CreateFilter, - }, - }, - }, - prelude::{AdditionOps, GraphViewOps, PropertyAdditionOps}, - }; - use raphtory_api::core::{ - entities::properties::prop::{IntoProp, Prop}, - storage::arc_str::ArcStr, - }; - use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, property_addition_ops::InternalPropertyAdditionOps, - }; - - fn list_u8(xs: &[u8]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::U8)) - } - fn list_u16(xs: &[u16]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::U16)) - } - fn list_u32(xs: &[u32]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::U32)) - } - fn list_u64(xs: &[u64]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::U64)) - } - fn list_i32(xs: &[i32]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::I32)) - } - fn list_i64(xs: &[i64]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::I64)) - } - fn list_f32(xs: &[f32]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::F32)) - } - fn list_f64(xs: &[f64]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::F64)) - } - fn list_str(xs: &[&str]) -> Prop { - Prop::list(xs.iter().map(|s| Prop::Str(ArcStr::from(*s)))) - } - fn list_bool(xs: &[bool]) -> Prop { - Prop::list(xs.iter().copied().map(Prop::Bool)) - } - - #[inline] - fn list(v: Vec) -> Prop { - Prop::List(v.into()) - } - - pub fn init_nodes_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let nodes: [(i64, &str, Vec<(&str, Prop)>); 12] = [ - ( - 1, - "n1", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u8s_max", list_u8(&[u8::MAX, u8::MAX])), // min: u8::MAX, max: u8::MAX, sum: 510 - ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u16s_max", list_u16(&[u16::MAX, u16::MAX])), // min: u16::MAX, max: u16::MAX, sum: 131070 - ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u32s_max", list_u32(&[u32::MAX, u32::MAX])), // min: 1, max: 3, sum: 8589934590 - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u64s_max", list_u64(&[u64::MAX, u64::MAX])), // min: 1, max: 3, sum: OVERFLOW - ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i64s", list_i64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 - ( - "nested_list", - list(vec![ - list(vec![ - list(vec![ - list(vec![50.0.into_prop(), 40.0.into_prop()]), - list(vec![60.0.into_prop()]), - ]), - list(vec![list(vec![46.0.into_prop()])]), - ]), - list(vec![list(vec![list(vec![90.0.into_prop()])])]), - ]), - ), - ], - ), - ( - 2, - "n1", - vec![ - ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 - ("p_bools", list_bool(&[true, true])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_u16s", list_u16(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_u32s", list_u32(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_i32s", list_i32(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_i64s", list_i64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5, 4.5])), // min: 1.0, max: 4.5, sum: 11.0, avg: 2.75, len: 4 - ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 - ], - ), - ( - 1, - "n2", - vec![ - ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 - ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 - ("p_bools", list_bool(&[false, false])), - ], - ), - ( - 2, - "n2", - vec![ - ("p_strs", list_str(&["a", "b", "c", "d"])), // min: None, max: None, sum: None, avg: None, len: 4 - ("p_u64s", list_u64(&[1, 2, 3, 4])), // min: 1, max: 4, sum: 10, avg: 2.5, len: 4 - ("p_f64s", list_f64(&[30.0, 50.0, 40.0])), // min: 30.0, max: 50.0, sum: 120.0, avg: 40.0, len: 3 - ], - ), - ( - 1, - "n3", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 1, 4])), // min: 1, max: 4, sum: 6, avg: 2.0, len: 3 - ("p_u16s", list_u16(&[1, 0, 5])), // min: 0, max: 5, sum: 6, avg: 2.0, len: 3 - ("p_u32s", list_u32(&[2, 2, 2])), // min: 2, max: 2, sum: 6, avg: 2.0, len: 3 - ("p_u64s", list_u64(&[0, 3, 3])), // min: 0, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i32s", list_i32(&[-1, 4, 3])), // min: -1, max: 4, sum: 6, avg: 2.0, len: 3 - ("p_i64s", list_i64(&[0, 3, -3])), // min: -3, max: 3, sum: 0, avg: 0.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.5, 3.0])), // min: 1.0, max: 3.0, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_f64s", list_f64(&[30.0, 60.0])), // min: 30.0, max: 60.0, sum: 90.0, avg: 45.0, len: 2 - ( - "nested_list", - list(vec![ - list(vec![ - list(vec![ - list(vec![50.0.into_prop(), 40.0.into_prop()]), - list(vec![60.0.into_prop()]), - ]), - list(vec![list(vec![46.0.into_prop()])]), - ]), - list(vec![list(vec![list(vec![90.0.into_prop()])])]), - ]), - ), - ], - ), - ( - 2, - "n3", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i64s", list_i64(&[1, 2, -3])), // min: -3, max: 2, sum: 0, avg: 0.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 - ( - "nested_list", - list(vec![ - list(vec![ - list(vec![ - list(vec![50.0.into_prop(), 40.0.into_prop()]), - list(vec![60.0.into_prop()]), - ]), - list(vec![list(vec![46.0.into_prop()])]), - ]), - list(vec![list(vec![list(vec![90.0.into_prop()])])]), - ]), - ), - ], - ), - ( - 1, - "n4", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_bools_all", list_bool(&[true, true])), - ], - ), - ( - 2, - "n4", - vec![ - ("p_strs", list_str(&["x", "y", "z"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[false, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u64s", list_u64(&[10, 20, 30])), // min: 10, max: 30, sum: 60, avg: 20.0, len: 3 - ("p_i32s", list_i32(&[10, 20, 30])), // min: 10, max: 30, sum: 60, avg: 20.0, len: 3 - ("p_f32s", list_f32(&[10.0, 20.0, 30.0])), // min: 10.0, max: 30.0, sum: 60.0, avg: 20.0, len: 3 - ("p_bools_all", list_bool(&[true, true])), - ], - ), - ( - 2, - "n5", - vec![ - ("p_u64s", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 - ("p_u64s_max", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 - ("p_u64s_min", list_u64(&[u64::MIN, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: 9223372036854775808.0, len: 2 - ("p_i64s", list_i64(&[i64::MAX, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 - ("p_i64s_max", list_i64(&[i64::MAX, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 - ("p_i64s_min", list_i64(&[i64::MIN, 1])), // min: 1, max: i64::MAX, sum: None (overflow), avg: 4611686018427387904.0, len: 2 - ], - ), - ( - 2, - "n6", - vec![ - ("p_i32s", list_i32(&[-2, 1, 3])), // min: -2, max: 3, sum: 2, avg: 0.6666666666666666, len: 3 - ], - ), - ( - 1, - "n7", - vec![ - ("p_u64s", list_u64(&[])), // min: None, max: None, sum: None, avg: None, len: 0 - ], - ), - ( - 2, - "n10", - vec![ - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ("p_bools", list_bool(&[true, false])), // min: None, max: None, sum: None, avg: None, len: 2 - ("p_u8s", list_u8(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u16s", list_u16(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u32s", list_u32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i32s", list_i32(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_i64s", list_i64(&[1, 2, -3])), // min: -3, max: 2, sum: 0, avg: 0.0, len: 3 - ("p_f32s", list_f32(&[1.0, 2.0, 3.5])), // min: 1.0, max: 3.5, sum: 6.5, avg: 2.1666666666666665, len: 3 - ("p_f64s", list_f64(&[50.0, 40.0])), // min: 40.0, max: 50.0, sum: 90.0, avg: 45.0, len: 2 - ("p_bools_all", list_bool(&[true, true])), - ], - ), - ]; - - for (t, id, props) in nodes { - graph.add_node(t, id, props, None, None).unwrap(); - } - - let metadata: [(&str, Vec<(&str, Prop)>); 8] = [ - ( - "n1", - vec![ - ("p_u8s", list_u8(&[2, 9])), // min: 2, max: 9, sum: 11, avg: 5.5, len: 2 - ("p_u16s", list_u16(&[3, 5])), // min: 3, max: 5, sum: 8, avg: 4.0, len: 2 - ("p_u32s", list_u32(&[4, 9])), // min: 4, max: 9, sum: 13, avg: 6.5, len: 2 - ], - ), - ( - "n2", - vec![ - ("p_u64s", list_u64(&[2, 3, 7])), // min: 2, max: 7, sum: 12, avg: 4.0, len: 3 - ], - ), - ( - "n3", - vec![ - ("p_i32s", list_i32(&[10, 2, -3])), // min: -3, max: 10, sum: 9, avg: 3.0, len: 3 - ("p_i64s", list_i64(&[1, 12, 3, 4])), // min: 1, max: 12, sum: 20, avg: 5.0, len: 4 - ], - ), - ( - "n4", - vec![ - ("p_f32s", list_f32(&[1.5, 2.5])), // min: 1.5, max: 2.5, sum: 4.0, avg: 2.0, len: 2 - ("p_f64s", list_f64(&[0.5, 1.5])), // min: 0.5, max: 1.5, sum: 2.0, avg: 1.0, len: 2 - ], - ), - ( - "n5", - vec![ - ("p_strs", list_str(&["m1", "m2", "m3"])), // min: None, max: None, sum: None, avg: None, len: 3 - ], - ), - ( - "n6", - vec![ - ("p_u64s", list_u64(&[])), // min: None, max: None, sum: None, avg: None, len: 0 - ("p_strs", list_str(&["a", "a"])), - ], - ), - ( - "n7", - vec![ - ("p_u64s", list_u64(&[u64::MAX, 1])), // min: 1, max: u64::MAX, sum: None (overflow), avg: ~9.22e18, len: 2 - ("p_strs", list_str(&["a"])), - ], - ), - ( - "n10", - vec![ - ("p_u64s", list_u64(&[1, 2, 3])), // min: 1, max: 3, sum: 6, avg: 2.0, len: 3 - ("p_strs", list_str(&["a", "b", "c"])), // min: None, max: None, sum: None, avg: None, len: 3 - ], - ), - ]; - - for (node_id, md) in metadata { - graph.node(node_id).unwrap().add_metadata(md).unwrap(); - } - - graph - } - - fn apply_assertion( - filter: impl TryAsCompositeFilter + CreateFilter + Clone, - expected: &[&str], - ) { - assert_filter_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected, - All, - ); - - assert_search_nodes_results( - init_nodes_graph, - IdentityGraphTransformer, - filter, - &expected, - All, - ); - } - - fn apply_assertion_err( - filter: impl TryAsCompositeFilter + CreateFilter + Clone, - expected: &str, - ) { - assert_filter_nodes_err( - init_nodes_graph, - IdentityGraphTransformer, - filter.clone(), - &expected, - All, - ); - - // assert_search_nodes_err( - // init_nodes_graph, - // IdentityGraphTransformer, - // filter, - // expected, - // All, - // ); - } - - // ------ Property: SUM ---- - #[test] - fn test_node_property_sum_u8s() { - let filter = NodeFilter.property("p_u8s").sum().eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_sum_u16s() { - let filter = NodeFilter.property("p_u16s").sum().eq(Prop::U64(6)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_sum_u32s() { - let filter = NodeFilter.property("p_u32s").sum().eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_sum_u64s() { - let filter = NodeFilter.property("p_u64s").sum().eq(Prop::U64(6)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_sum_i32s() { - let filter = NodeFilter.property("p_i32s").sum().eq(Prop::I64(2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_sum_i64s() { - let filter = NodeFilter.property("p_i64s").sum().eq(Prop::I64(0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_sum_f32s() { - let filter = NodeFilter.property("p_f32s").sum().eq(Prop::F64(6.5)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_sum_f64s() { - let filter = NodeFilter.property("p_f64s").sum().eq(Prop::F64(120.0)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } - - // ------ Property: AVG ---- - #[test] - fn test_node_property_avg_u8s() { - let filter = NodeFilter.property("p_u8s").avg().eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_avg_u16s() { - let filter = NodeFilter.property("p_u16s").avg().eq(Prop::F64(2.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_avg_u32s() { - let filter = NodeFilter.property("p_u32s").avg().eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_avg_u64s() { - let filter = NodeFilter.property("p_u64s").avg().eq(Prop::F64(2.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .avg() - .eq(Prop::F64(0.6666666666666666)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_avg_i64s() { - let filter = NodeFilter.property("p_i64s").avg().eq(Prop::F64(0.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .avg() - .eq(Prop::F64(2.1666666666666665)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_avg_f64s() { - let filter = NodeFilter.property("p_f64s").avg().eq(Prop::F64(40.0)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } - - // ------ Property: LEN ------ - #[test] - fn test_node_property_len_u8s() { - let filter = NodeFilter.property("p_u8s").len().eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_len_u16s() { - let filter = NodeFilter.property("p_u16s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_len_u32s() { - let filter = NodeFilter.property("p_u32s").len().eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_len_u64s() { - let filter = NodeFilter.property("p_u64s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_len_i32s() { - let filter = NodeFilter.property("p_i32s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_len_i64s() { - let filter = NodeFilter.property("p_i64s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_len_f32s() { - let filter = NodeFilter.property("p_f32s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_len_f64s() { - let filter = NodeFilter.property("p_f64s").len().eq(Prop::U64(3)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_len_strs() { - let filter = NodeFilter.property("p_strs").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - // ------ Property: MIN ------ - #[test] - fn test_node_property_min_u8s() { - let filter = NodeFilter.property("p_u8s").min().eq(Prop::U8(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_min_u16s() { - let filter = NodeFilter.property("p_u16s").min().eq(Prop::U16(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_min_u32s() { - let filter = NodeFilter.property("p_u32s").min().eq(Prop::U32(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_min_u64s() { - let filter = NodeFilter.property("p_u64s").min().eq(Prop::U64(1)); - let expected = vec!["n1", "n10", "n2", "n3", "n5"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_min_i32s() { - let filter = NodeFilter.property("p_i32s").min().eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_min_i64s() { - let filter = NodeFilter.property("p_i64s").min().eq(Prop::I64(-3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_min_f32s() { - let filter = NodeFilter.property("p_f32s").min().eq(Prop::F32(10.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_min_f64s() { - let filter = NodeFilter.property("p_f64s").min().eq(Prop::F64(40.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Property: MAX ------ - #[test] - fn test_node_property_max_u8s() { - let filter = NodeFilter.property("p_u8s").max().eq(Prop::U8(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_max_u16s() { - let filter = NodeFilter.property("p_u16s").max().eq(Prop::U16(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_max_u32s() { - let filter = NodeFilter.property("p_u32s").max().eq(Prop::U32(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_max_u64s() { - let filter = NodeFilter.property("p_u64s").max().eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_max_i32s() { - let filter = NodeFilter.property("p_i32s").max().eq(Prop::I32(3)); - let expected = vec!["n10", "n3", "n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_max_i64s() { - let filter = NodeFilter.property("p_i64s").max().eq(Prop::I64(2)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_max_f32s() { - let filter = NodeFilter.property("p_f32s").max().eq(Prop::F32(30.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_max_f64s() { - let filter = NodeFilter.property("p_f64s").max().eq(Prop::F64(50.0)); - let expected = vec!["n1", "n10", "n2", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Metadata: SUM ------ - #[test] - fn test_node_property_metadata_sum_u8s() { - let filter = NodeFilter.metadata("p_u8s").sum().eq(Prop::U64(11)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_sum_u16s() { - let filter = NodeFilter.metadata("p_u16s").sum().eq(Prop::U64(8)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_sum_u32s() { - let filter = NodeFilter.metadata("p_u32s").sum().eq(Prop::U64(13)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_sum_u64s() { - let filter = NodeFilter.metadata("p_u64s").sum().eq(Prop::U64(12)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_sum_i32s() { - let filter = NodeFilter.metadata("p_i32s").sum().eq(Prop::I64(9)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_sum_i64s() { - let filter = NodeFilter.metadata("p_i64s").sum().eq(Prop::I64(20)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_sum_f32s() { - let filter = NodeFilter.metadata("p_f32s").sum().eq(Prop::F64(4.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_sum_f64s() { - let filter = NodeFilter.metadata("p_f64s").sum().eq(Prop::F64(2.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - // ------ Metadata: AVG ------ - #[test] - fn test_node_property_metadata_avg_u8s() { - let filter = NodeFilter.metadata("p_u8s").avg().eq(Prop::F64(5.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_avg_u16s() { - let filter = NodeFilter.metadata("p_u16s").avg().eq(Prop::F64(4.0)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_avg_u32s() { - let filter = NodeFilter.metadata("p_u32s").avg().eq(Prop::F64(6.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_avg_u64s() { - let filter = NodeFilter.metadata("p_u64s").avg().eq(Prop::F64(4.0)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_avg_i32s() { - let filter = NodeFilter.metadata("p_i32s").avg().eq(Prop::F64(3.0)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_avg_i64s() { - let filter = NodeFilter.metadata("p_i64s").avg().eq(Prop::F64(5.0)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_avg_f32s() { - let filter = NodeFilter.metadata("p_f32s").avg().eq(Prop::F64(2.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_avg_f64s() { - let filter = NodeFilter.metadata("p_f64s").avg().eq(Prop::F64(1.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - // ------ Metadata: MIN ------ - #[test] - fn test_node_property_metadata_min_u8s() { - let filter = NodeFilter.metadata("p_u8s").min().eq(Prop::U8(2)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_min_u16s() { - let filter = NodeFilter.metadata("p_u16s").min().eq(Prop::U16(3)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_min_u32s() { - let filter = NodeFilter.metadata("p_u32s").min().eq(Prop::U32(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_min_u64s() { - let filter = NodeFilter.metadata("p_u64s").min().eq(Prop::U64(2)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_min_i32s() { - let filter = NodeFilter.metadata("p_i32s").min().eq(Prop::I32(-3)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_min_i64s() { - let filter = NodeFilter.metadata("p_i64s").min().eq(Prop::I64(1)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_min_f32s() { - let filter = NodeFilter.metadata("p_f32s").min().eq(Prop::F32(1.5)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_min_f64s() { - let filter = NodeFilter.metadata("p_f64s").min().eq(Prop::F64(0.5)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - // ------ Metadata: MAX ------ - #[test] - fn test_node_property_metadata_max_u8s() { - let filter = NodeFilter.metadata("p_u8s").max().eq(Prop::U8(9)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_max_u16s() { - let filter = NodeFilter.metadata("p_u16s").max().eq(Prop::U16(5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_max_u32s() { - let filter = NodeFilter.metadata("p_u32s").max().eq(Prop::U32(9)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_max_u64s() { - let filter = NodeFilter.metadata("p_u64s").max().eq(Prop::U64(7)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_max_i32s() { - let filter = NodeFilter.metadata("p_i32s").max().eq(Prop::I32(10)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_max_i64s() { - let filter = NodeFilter.metadata("p_i64s").max().eq(Prop::I64(12)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_max_f32s() { - let filter = NodeFilter.metadata("p_f32s").max().eq(Prop::F32(2.5)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_max_f64s() { - let filter = NodeFilter.metadata("p_f64s").max().eq(Prop::F64(1.5)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - // ------ Metadata: Len ------ - #[test] - fn test_node_property_metadata_len_u8s() { - let filter = NodeFilter.metadata("p_u8s").len().eq(Prop::U64(2)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_len_u16s() { - let filter = NodeFilter.metadata("p_u16s").len().eq(Prop::U64(2)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_len_u32s() { - let filter = NodeFilter.metadata("p_u32s").len().eq(Prop::U64(2)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_len_u64s() { - let filter = NodeFilter.metadata("p_u64s").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_len_i32s() { - let filter = NodeFilter.metadata("p_i32s").len().eq(Prop::U64(3)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_len_i64s() { - let filter = NodeFilter.metadata("p_i64s").len().eq(Prop::U64(4)); - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_len_f32s() { - let filter = NodeFilter.metadata("p_f32s").len().eq(Prop::U64(2)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_len_f64s() { - let filter = NodeFilter.metadata("p_f64s").len().eq(Prop::U64(2)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_metadata_len_strs() { - let filter = NodeFilter.metadata("p_strs").len().eq(Prop::U64(3)); - let expected = vec!["n10", "n5"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal last: SUM ------ - #[test] - fn test_node_property_temporal_last_sum_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_sum_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_sum_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_sum_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .sum() - .eq(Prop::U64(60)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_sum_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .sum() - .eq(Prop::I64(60)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_sum_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .sum() - .eq(Prop::I64(0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_sum_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .sum() - .eq(Prop::F64(6.5)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_sum_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .sum() - .eq(Prop::F64(90.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal last: AVG ------ - #[test] - fn test_node_property_temporal_last_avg_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_avg_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_avg_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_avg_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .avg() - .eq(Prop::F64(20.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .avg() - .eq(Prop::F64(0.6666666666666666)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_avg_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .avg() - .eq(Prop::F64(0.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .avg() - .eq(Prop::F64(20.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_avg_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .avg() - .eq(Prop::F64(45.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal last: MIN ------ - #[test] - fn test_node_property_temporal_last_min_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .min() - .eq(Prop::U8(1)); - let expected = vec!["n1", "n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_min_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .min() - .eq(Prop::U16(1)); - let expected = vec!["n1", "n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_min_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .min() - .eq(Prop::U32(1)); - let expected = vec!["n1", "n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_min_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .min() - .eq(Prop::U64(10)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_min_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .min() - .eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_min_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .min() - .eq(Prop::I64(-3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_min_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .min() - .eq(Prop::F32(10.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_min_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .min() - .eq(Prop::F64(40.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal last: MAX ------ - #[test] - fn test_node_property_temporal_last_max_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .max() - .eq(Prop::U8(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_max_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .max() - .eq(Prop::U16(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_max_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .max() - .eq(Prop::U32(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_max_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .max() - .eq(Prop::U64(30)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_max_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .max() - .eq(Prop::I32(3)); - let expected = vec!["n3", "n6", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_max_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_max_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .max() - .eq(Prop::F32(3.5)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_max_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .max() - .eq(Prop::F64(50.0)); - let expected = vec!["n1", "n2", "n3", "n10"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal last: LEN ------ - #[test] - fn test_node_property_temporal_last_len_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_len_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_len_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_len_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n4", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_len_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n4", "n6", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_len_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_len_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n3", "n4", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_last_len_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .last() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal all: SUM ------ - #[test] - fn test_node_property_temporal_all_sum_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_sum_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_sum_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_sum_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_sum_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .sum() - .eq(Prop::I64(6)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_sum_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .sum() - .eq(Prop::I64(0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_sum_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .sum() - .eq(Prop::F64(6.5)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_sum_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .sum() - .eq(Prop::F64(90.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal all: AVG ------ - #[test] - fn test_node_property_temporal_all_avg_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_avg_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_avg_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_avg_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_avg_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .avg() - .eq(Prop::F64(0.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .avg() - .eq(Prop::F64(2.1666666666666665)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_avg_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .avg() - .eq(Prop::F64(45.0)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal all: MIN ------ - #[test] - fn test_node_property_temporal_all_min_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .min() - .eq(Prop::U8(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_min_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .min() - .eq(Prop::U16(1)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_min_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .min() - .eq(Prop::U32(1)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_min_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .min() - .eq(Prop::U64(1)); - let expected = vec!["n1", "n10", "n2", "n5"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_min_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .min() - .eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_min_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .min() - .eq(Prop::I64(-3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_min_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .min() - .eq(Prop::F32(1.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_min_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .min() - .eq(Prop::F64(30.0)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal all: MAX ------ - #[test] - fn test_node_property_temporal_all_max_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .max() - .eq(Prop::U8(3)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_max_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .max() - .eq(Prop::U16(3)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_max_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .max() - .eq(Prop::U32(3)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_max_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .max() - .eq(Prop::U64(4)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_max_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .max() - .eq(Prop::I32(3)); - let expected = vec!["n10", "n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_max_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_max_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .max() - .eq(Prop::F32(3.5)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_max_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .max() - .eq(Prop::F64(50.0)); - let expected = vec!["n1", "n10", "n2"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal all: LEN ------ - #[test] - fn test_node_property_temporal_all_len_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_len_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_len_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_len_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .all() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_len_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_len_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .all() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_len_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .all() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_all_len_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .all() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal first: SUM ------ - #[test] - fn test_node_property_temporal_first_sum_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_sum_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_sum_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_sum_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_sum_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .sum() - .eq(Prop::I64(6)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_sum_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .sum() - .eq(Prop::I64(0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_sum_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .sum() - .eq(Prop::F64(6.5)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_sum_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .sum() - .eq(Prop::F64(90.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal first: AVG ------ - #[test] - fn test_node_property_temporal_first_avg_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_avg_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_avg_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_avg_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_avg_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .avg() - .eq(Prop::F64(0.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .avg() - .eq(Prop::F64(2.1666666666666665)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_avg_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .avg() - .eq(Prop::F64(45.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal first: MIN ------ - #[test] - fn test_node_property_temporal_first_min_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .min() - .eq(Prop::U8(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_min_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .min() - .eq(Prop::U16(1)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_min_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .min() - .eq(Prop::U32(1)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_min_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .min() - .eq(Prop::U64(1)); - let expected = vec!["n1", "n10", "n2", "n4", "n5"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_min_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .min() - .eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_min_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .min() - .eq(Prop::I64(-3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_min_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .min() - .eq(Prop::F32(1.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_min_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .min() - .eq(Prop::F64(30.0)); - let expected = vec!["n2", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal first: MAX ------ - #[test] - fn test_node_property_temporal_first_max_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .max() - .eq(Prop::U8(3)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_max_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .max() - .eq(Prop::U16(3)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_max_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .max() - .eq(Prop::U32(3)); - let expected = vec!["n1", "n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_max_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .max() - .eq(Prop::U64(4)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_max_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .max() - .eq(Prop::I32(3)); - let expected = vec!["n1", "n10", "n4", "n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_max_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n10"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_max_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .max() - .eq(Prop::F32(3.5)); - let expected = vec!["n1", "n10", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_max_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .max() - .eq(Prop::F64(50.0)); - let expected = vec!["n1", "n10", "n2"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal first: LEN ------ - #[test] - fn test_node_property_temporal_first_len_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_len_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_len_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_len_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_len_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_len_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .first() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_len_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .first() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_first_len_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .first() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal any: SUM ------ - #[test] - fn test_node_property_temporal_any_sum_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_sum_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_sum_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_sum_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .sum() - .eq(Prop::U64(6)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .sum() - .eq(Prop::U64(10)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_sum_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .sum() - .eq(Prop::I64(6)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .sum() - .eq(Prop::I64(60)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_sum_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .sum() - .eq(Prop::I64(0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .sum() - .eq(Prop::I64(10)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_sum_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .sum() - .eq(Prop::F64(6.5)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .sum() - .eq(Prop::F64(60.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_sum_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .sum() - .eq(Prop::F64(90.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .sum() - .eq(Prop::F64(120.0)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal any: AVG ------ - #[test] - fn test_node_property_temporal_any_avg_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_avg_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_avg_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_avg_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_avg_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_avg_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(0.0)); - let expected = vec!["n3", "n10"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.5)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_avg_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(2.1666666666666665)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .avg() - .eq(Prop::F64(20.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_avg_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(45.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .avg() - .eq(Prop::F64(40.0)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal any: MIN ------ - #[test] - fn test_node_property_temporal_any_min_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .min() - .eq(Prop::U8(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_min_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .min() - .eq(Prop::U16(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_min_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .min() - .eq(Prop::U32(1)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_min_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .min() - .eq(Prop::U64(1)); - let expected = vec!["n1", "n10", "n2", "n3", "n4", "n5"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_min_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .min() - .eq(Prop::I32(-2)); - let expected = vec!["n6"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .min() - .eq(Prop::I32(10)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_min_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .min() - .eq(Prop::I64(-3)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .min() - .eq(Prop::I64(1)); - let expected = vec!["n1", "n5"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_min_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .min() - .eq(Prop::F32(1.0)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .min() - .eq(Prop::F32(10.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_min_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .min() - .eq(Prop::F64(30.0)); - let expected = vec!["n1", "n2", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .min() - .eq(Prop::F64(40.0)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal any: MAX ------ - #[test] - fn test_node_property_temporal_any_max_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .max() - .eq(Prop::U8(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .max() - .eq(Prop::U8(4)); - let expected = vec!["n1", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_max_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .max() - .eq(Prop::U16(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .max() - .eq(Prop::U16(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_max_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .max() - .eq(Prop::U32(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .max() - .eq(Prop::U32(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_max_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .max() - .eq(Prop::U64(4)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .max() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_max_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .max() - .eq(Prop::I32(3)); - let expected = vec!["n1", "n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .max() - .eq(Prop::I32(30)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_max_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .max() - .eq(Prop::I64(2)); - let expected = vec!["n10", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_max_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .max() - .eq(Prop::F32(3.5)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .max() - .eq(Prop::F32(30.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_max_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .max() - .eq(Prop::F64(50.0)); - let expected = vec!["n1", "n10", "n2", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal any: LEN ------ - #[test] - fn test_node_property_temporal_any_len_u8s() { - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .len() - .is_in(vec![Prop::U64(3)]); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u8s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_len_u16s() { - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_len_u32s() { - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_len_u64s() { - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_len_i32s() { - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4", "n6"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i32s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_len_i64s() { - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_i64s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_len_f32s() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .len() - .eq(Prop::U64(4)); - let expected = vec!["n1"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_property_temporal_any_len_f64s() { - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .len() - .eq(Prop::U64(2)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f64s") - .temporal() - .any() - .len() - .eq(Prop::U64(3)); - let expected = vec!["n1", "n2"]; - apply_assertion(filter, &expected); - } - - // ------ EMPTY LISTS ------ - #[test] - fn test_empty_list_agg() { - let filter = NodeFilter.property("p_u64s").sum().eq(Prop::U64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_u64s").avg().eq(Prop::F64(0.0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .last() - .min() - .eq(Prop::U64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s") - .temporal() - .first() - .max() - .eq(Prop::U64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_u64s").len().eq(Prop::U64(0)); - let expected: Vec<&str> = vec!["n7"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.metadata("p_u64s").len().eq(Prop::U64(0)); - let expected: Vec<&str> = vec!["n6"]; - apply_assertion(filter, &expected); - } - - // ------ Unsupported filter operations ------ - #[test] - fn test_unsupported_filter_ops_agg() { - let filter = NodeFilter.property("p_u64s").sum().starts_with("abc"); - let expected: &str = "Operator STARTS_WITH is not supported with list aggregation"; - apply_assertion_err(filter, expected); - - let filter = NodeFilter.property("p_u64s").avg().ends_with("abc"); - let expected: &str = "Operator ENDS_WITH is not supported with list aggregation"; - apply_assertion_err(filter, expected); - - let filter = NodeFilter.property("p_u64s").min().is_none(); - let expected: &str = "Operator IS_NONE is not supported with list aggregation"; - apply_assertion_err(filter, expected); - - let filter = NodeFilter.property("p_u64s").max().is_some(); - let expected: &str = "Operator IS_SOME is not supported with list aggregation"; - apply_assertion_err(filter, expected); - - let filter = NodeFilter.property("p_u64s").len().contains("abc"); - let expected: &str = "Operator CONTAINS is not supported with list aggregation"; - apply_assertion_err(filter, expected); - - let filter = NodeFilter.property("p_u64s").sum().not_contains("abc"); - let expected: &str = "Operator NOT_CONTAINS is not supported with list aggregation"; - apply_assertion_err(filter, expected); - } - - // --------------- OVERFLOW --------------- - #[test] - fn test_max_value_agg() { - let filter = NodeFilter - .property("p_u64s_max") - .max() - .eq(Prop::U64(u64::MAX)); - let expected: Vec<&str> = vec!["n5", "n1"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u64s_min") - .min() - .eq(Prop::U64(u64::MIN)); - let expected: Vec<&str> = vec!["n5"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_u8s_max").sum().eq(Prop::U64(510)); - let expected: Vec<&str> = vec!["n1"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u16s_max") - .sum() - .eq(Prop::U64(131070)); - let expected: Vec<&str> = vec!["n1"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_u32s_max") - .sum() - .eq(Prop::U64(8589934590)); - let expected: Vec<&str> = vec!["n1"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_u64s_max").sum().gt(Prop::U64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - // AVG is computed in f64 even if SUM overflowed. - let avg = (u64::MAX as f64 + 1.0) / 2.0; - let filter = NodeFilter.property("p_u64s_max").avg().eq(avg); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter.property("p_i64s_max").sum().gt(Prop::I64(0)); - let expected: Vec<&str> = vec![]; - apply_assertion(filter, &expected); - - // AVG is computed in f64 even if SUM overflowed. - let avg = (i64::MAX as f64 + 1.0) / 2.0; - let filter = NodeFilter.property("p_i64s_max").avg().eq(avg); - let expected = vec!["n5"]; - apply_assertion(filter, &expected); - } - - // ------ Property: any ------ - #[test] - fn test_node_property_any() { - let filter = NodeFilter.property("p_u8s").any().eq(Prop::U8(3)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Property: all ------ - #[test] - fn test_node_property_all() { - let filter = NodeFilter - .property("p_bools_all") - .all() - .eq(Prop::Bool(true)); - let expected = vec!["n10", "n4"]; - apply_assertion(filter, &expected); - } - - // ------ Metadata: any ------ - #[test] - fn test_node_metadata_any() { - let filter = NodeFilter.metadata("p_u64s").any().eq(Prop::U64(1)); - let expected = vec!["n10", "n7"]; - apply_assertion(filter, &expected); - } - - // ------ Metadata: all ------ - #[test] - fn test_node_metadata_all() { - let filter = NodeFilter.metadata("p_strs").all().eq("a"); - let expected = vec!["n6", "n7"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal First: any ------ - #[test] - fn test_node_temporal_property_first_any() { - let filter = NodeFilter - .property("p_bools") - .temporal() - .first() - .any() - .eq(false); - let expected = vec!["n1", "n10", "n2", "n3", "n4"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal First: all ------ - #[test] - fn test_node_temporal_property_first_all() { - let filter = NodeFilter - .property("p_bools_all") - .temporal() - .first() - .all() - .eq(true); - let expected = vec!["n10", "n4"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal last: any ------ - #[test] - fn test_node_temporal_property_last_any() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .last() - .any() - .eq(Prop::F32(3.5)); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal last: all ------ - #[test] - fn test_node_temporal_property_last_all() { - let filter = NodeFilter - .property("p_bools_all") - .temporal() - .last() - .all() - .eq(true); - let expected = vec!["n10", "n4"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal Any: any ------ - #[test] - fn test_node_temporal_property_any_any() { - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .any() - .eq(Prop::F32(3.5)); - let expected = vec!["n1", "n10", "n3", "n4"]; - apply_assertion(filter, &expected); - - let filter = NodeFilter - .property("p_f32s") - .temporal() - .any() - .any() - .eq(Prop::F32(30.0)); - let expected = vec!["n4"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal Any: all ------ - #[test] - fn test_node_temporal_property_any_all() { - let filter = NodeFilter - .property("p_bools") - .temporal() - .any() - .all() - .eq(false); - let expected = vec!["n2", "n4"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_nested_list_property_all_all_all_any() { - let filter = NodeFilter - .property("nested_list") - .all() - .all() - .all() - .any() - .gt(45.0); - - let expected = vec!["n1", "n3"]; - apply_assertion(filter, &expected); - } - - #[test] - fn test_node_nested_list_temporal_property_all_all_all_all_any() { - let filter = NodeFilter - .property("nested_list") - .temporal() - .all() - .all() - .all() - .all() - .any() - .gt(45.0); - - let expected = vec!["n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal All: any ------ - #[test] - fn test_node_temporal_property_all_any() { - let filter = NodeFilter - .property("p_bools") - .temporal() - .all() - .any() - .eq(true); - let expected = vec!["n1", "n10", "n3"]; - apply_assertion(filter, &expected); - } - - // ------ Temporal All: all ------ - #[test] - fn test_node_temporal_property_all_all() { - let filter = NodeFilter - .property("p_bools_all") - .temporal() - .all() - .all() - .eq(true); - let expected = vec!["n4", "n10"]; - apply_assertion(filter, &expected); - } - } - - #[cfg(test)] - mod test_edge_filter { - use crate::test::{ - init_edges_graph, init_edges_graph_with_num_ids, init_edges_graph_with_str_ids, - init_edges_graph_with_str_ids_del, init_nodes_graph, IdentityGraphTransformer, - }; - use raphtory::db::graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, - assert_select_edges_results, TestGraphVariants, TestVariants, - }, - views::filter::model::{ - edge_filter::EdgeFilter, - node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, - property_filter::ops::{ListAggOps, PropertyFilterOps}, - ComposableFilter, EdgeViewFilterOps, PropertyFilterFactory, - TemporalPropertyFilterFactory, ViewWrapOps, - }, - }; - - #[test] - fn test_filter_edges_src_property_eq() { - let filter = EdgeFilter::src().property("p10").eq("Paper_airplane"); - let expected_results = vec!["1->2", "3->1"]; - let g = |g| init_edges_graph(init_nodes_graph(g)); - assert_filter_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_src_property_temporal_eq() { - let filter = EdgeFilter::src() - .property("p30") - .temporal() - .first() - .eq("Old_boat"); - let expected_results = vec!["2->1", "2->3"]; - let g = |g| init_edges_graph(init_nodes_graph(g)); - assert_filter_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_src_metadata_eq() { - let filter = EdgeFilter::src().metadata("m1").eq("pometry"); - let expected_results = vec!["1->2"]; - let g = |g| init_edges_graph(init_nodes_graph(g)); - assert_filter_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - g, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_eq() { - let filter = EdgeFilter::src().name().eq("3"); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_ne() { - let filter = EdgeFilter::src().name().ne("1"); - let expected_results = vec![ - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_in() { - let filter = EdgeFilter::src().name().is_in(vec!["1"]); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().is_in(vec!["1", "2"]); - let expected_results = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_not_in() { - let filter = EdgeFilter::src().name().is_not_in(vec!["1"]); - let expected_results = vec![ - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_eq() { - let filter = EdgeFilter::dst().name().eq("2"); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_ne() { - let filter = EdgeFilter::dst().name().ne("2"); - let expected_results = vec![ - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_in() { - let filter = EdgeFilter::dst().name().is_in(vec!["2"]); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().is_in(vec!["2", "3"]); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_not_in() { - let filter = EdgeFilter::dst().name().is_not_in(vec!["1"]); - let expected_results = vec![ - "1->2", - "2->3", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_dst_starts_with() { - let filter = EdgeFilter::src().name().starts_with("Joh"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().starts_with("Joker"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().starts_with("Jimmy"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().starts_with("Tango"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_dst_ends_with() { - let filter = EdgeFilter::src().name().ends_with("Mayer"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().ends_with("Cruise"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().ends_with("Page"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().name().ends_with("Cruise"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_contains() { - let filter = EdgeFilter::src().name().contains("Mayer"); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_contains_not() { - let filter = EdgeFilter::src().name().not_contains("Mayer"); - let expected_results: Vec<&str> = - vec!["1->2", "2->1", "2->3", "3->1", "David Gilmour->John Mayer"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_fuzzy_search() { - let filter = EdgeFilter::src().name().fuzzy_search("John", 2, true); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().fuzzy_search("John", 2, false); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().name().fuzzy_search("John May", 2, false); - let expected_results: Vec<&str> = vec!["John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_not_src() { - let filter = EdgeFilter::src().name().is_not_in(vec!["1"]).not(); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_id_eq() { - let filter = EdgeFilter::src().id().eq("3"); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().id().eq(3); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_id_eq() { - let filter = EdgeFilter::dst().id().eq("3"); - let expected_results = vec!["2->3"]; - // assert_filter_edges_results( - // init_edges_graph, - // IdentityGraphTransformer, - // filter.clone(), - // &expected_results, - // TestVariants::All, - // ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().id().eq(3); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_id_ne() { - let filter = EdgeFilter::src().id().ne("3"); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().id().ne(3); - let expected_results = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_id_ne() { - let filter = EdgeFilter::dst().id().ne("3"); - let expected_results = vec![ - "1->2", - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().id().ne(3); - let expected_results = vec!["1->2", "2->1", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_id_src_is_in() { - let filter = EdgeFilter::src().id().is_in(vec!["3"]); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().id().is_in(vec![3]); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_id_dst_is_in() { - let filter = EdgeFilter::dst().id().is_in(vec!["3"]); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().id().is_in(vec![3]); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_id_src_is_not_in() { - let filter = EdgeFilter::src().id().is_not_in(vec!["3"]); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src().id().is_not_in(vec![3]); - let expected_results = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_id_dst_is_not_in() { - let filter = EdgeFilter::dst().id().is_not_in(vec!["3"]); - let expected_results = vec![ - "1->2", - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst().id().is_not_in(vec![3]); - let expected_results = vec!["1->2", "2->1", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_id_lt() { - let filter = EdgeFilter::src().id().lt(3); - let expected_results = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_id_lt() { - let filter = EdgeFilter::dst().id().lt(3); - let expected_results = vec!["1->2", "2->1", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_id_le() { - let filter = EdgeFilter::src().id().le(3); - let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_id_le() { - let filter = EdgeFilter::dst().id().le(3); - let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_id_gt() { - let filter = EdgeFilter::src().id().gt(1); - let expected_results = vec!["2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_id_gt() { - let filter = EdgeFilter::dst().id().gt(1); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_id_ge() { - let filter = EdgeFilter::src().id().ge(1); - let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_id_ge() { - let filter = EdgeFilter::dst().id().ge(1); - let expected_results = vec!["1->2", "2->1", "2->3", "3->1"]; - assert_filter_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_num_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_name_starts_with() { - let filter = EdgeFilter::src().name().starts_with("Tw"); - let expected_results = vec!["Two->One", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_id_ends_with() { - let filter = EdgeFilter::src().id().ends_with("don"); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_id_contains() { - let filter = EdgeFilter::src().id().contains("don"); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_id_contains() { - let filter = EdgeFilter::dst().id().contains("Par"); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_name_not_contains() { - let filter = EdgeFilter::dst().name().not_contains("Par"); - let expected_results = vec![ - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - "Three->One", - "Two->One", - "Two->Three", - ]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_src_id_is_in() { - let filter = EdgeFilter::src().id().is_in(["Two"]); - let expected_results = vec!["Two->One", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_dst_id_is_not_in() { - let filter = EdgeFilter::dst().id().is_not_in(["One"]); - let expected_results = vec![ - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - "London->Paris", - "Two->Three", - ]; - assert_filter_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_is_active_edge_window() { - let filter = EdgeFilter.window(1, 3).is_active(); - let expected_results = vec!["London->Paris", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_is_active_edge_after() { - let filter = EdgeFilter.after(3).is_active(); - let expected_results = vec![ - "Bangalore->Bangalore", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_is_active_edge_before() { - let filter = EdgeFilter.before(3).is_active(); - let expected_results = vec!["London->Paris", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_is_active_edge_snapshot_latest() { - let filter = EdgeFilter.snapshot_latest().is_active(); - let expected_results = vec!["Bangalore->Bangalore"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_is_valid_edge_window() { - let filter = EdgeFilter.window(1, 3).is_valid(); - let expected_results = vec!["London->Paris", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - - let filter = EdgeFilter.window(1, 4).is_valid(); - let expected_results = vec!["Three->One", "Two->One", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - } - - #[test] - fn test_is_valid_edge_snapshot_at() { - let filter = EdgeFilter.snapshot_at(2).is_valid(); - let expected_results = vec!["London->Paris", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - - let filter = EdgeFilter.snapshot_at(3).is_valid(); - let expected_results = vec!["Three->One", "Two->One", "Two->Three"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - } - - #[test] - fn test_is_valid_edge_snapshot_latest() { - let filter = EdgeFilter.snapshot_latest().is_valid(); - let expected_results = vec![ - "Bangalore->Bangalore", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - "Three->One", - "Two->One", - "Two->Three", - ]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestGraphVariants::PersistentGraph, - ); - } - - // Disk graph doesn't support deletions - #[test] - fn test_is_deleted_edge_after() { - let filter = EdgeFilter.after(1).is_deleted(); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - // Disk graph doesn't support deletions - #[test] - fn test_is_deleted_edge_before() { - let filter = EdgeFilter.before(4).is_deleted(); - let expected_results = vec!["London->Paris"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_is_self_loop_edge_window() { - let filter = EdgeFilter.window(1, 3).is_self_loop(); - let expected_results = vec![]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.window(1, 6).is_self_loop(); - let expected_results = vec!["Bangalore->Bangalore"]; - assert_filter_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_select_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph_with_str_ids_del, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - } - - #[cfg(test)] - mod test_edge_property_filter { - use crate::test::{init_edges_graph, init_edges_graph2, IdentityGraphTransformer}; - use raphtory::db::graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, - TestVariants, - }, - views::filter::model::{ - edge_filter::EdgeFilter, - property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, - ComposableFilter, PropertyFilterFactory, TemporalPropertyFilterFactory, - ViewWrapOps, - }, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - #[test] - fn test_filter_edges_for_property_eq() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").eq(2u64); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p30").temporal().first().eq("Old_boat"); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p20").temporal().all().eq("Gold_ship"); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_ne() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").ne(2u64); - let expected_results = vec![ - "1->2", - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p30").temporal().first().ne("Old_boat"); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p30").temporal().all().ne("Classic"); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_lt() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").lt(10u64); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().lt(5u64); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().all().lt(10u64); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_le() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").le(6u64); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().le(3u64); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().all().le(5u64); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_gt() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").gt(2u64); - let expected_results = vec![ - "1->2", - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().gt(5u64); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().all().gt(5u64); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_ge() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").ge(2u64); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().ge(6u64); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().all().ge(6u64); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_in() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").is_in(vec![Prop::U64(6)]); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .is_in(vec![Prop::U64(2), Prop::U64(6)]); - let expected_results = vec![ - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .temporal() - .first() - .is_in(vec![Prop::U64(6)]); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .temporal() - .all() - .is_in(vec![Prop::U64(6)]); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_not_in() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .temporal() - .first() - .is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .temporal() - .all() - .is_not_in(vec![Prop::U64(6)]); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_is_some() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p2").is_some(); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p2").temporal().first().is_some(); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_is_none() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. - let filter = EdgeFilter.property("p2").is_none(); - let expected_results = Vec::<&str>::new(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("p2").temporal().first().is_none(); - let expected_results = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_starts_with() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p10").starts_with("Pa"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .any() - .starts_with("Pape"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .starts_with("Paper"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .starts_with("Traffic"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p30") - .temporal() - .first() - .starts_with("Old"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .all() - .starts_with("Gold"); - let expected_results: Vec<&str> = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_ends_with() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p10").ends_with("lane"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .any() - .ends_with("ship"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .ends_with("ane"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .ends_with("marcus"); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .first() - .ends_with("boat"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .all() - .ends_with("ship"); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_contains() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p10").contains("Paper"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .any() - .contains("Paper"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .contains("Paper"); - let expected_results: Vec<&str> = vec!["1->2", "2->1", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .first() - .contains("boat"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p20").temporal().all().contains("ship"); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_for_property_contains_not() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("p10").not_contains("ship"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .any() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p10") - .temporal() - .last() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["1->2", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p20") - .temporal() - .first() - .not_contains("boat"); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p30") - .temporal() - .all() - .not_contains("ship"); - let expected_results: Vec<&str> = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_filter_edges_by_fuzzy_search() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. - // TODO: Enable these test for event_disk_graph, persistent_disk_graph once string property is fixed. - let filter = EdgeFilter.property("p1").fuzzy_search("shiv", 2, true); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = EdgeFilter.property("p1").fuzzy_search("ShiV", 2, true); - let expected_results: Vec<&str> = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = EdgeFilter.property("p1").fuzzy_search("shiv", 2, false); - let expected_results: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_filter_edges_for_not_property() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. - let filter = EdgeFilter.property("p2").ne(2u64).not(); - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_window_filter() { - let filter = EdgeFilter - .window(1, 3) - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .window(1, 5) - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec![ - "1->2", - "2->3", - "3->1", - "2->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_window_filter_on_non_temporal_property() { - let filter1 = EdgeFilter.window(1, 2).property("p1").eq("shivam_kapoor"); - let filter2 = EdgeFilter - .window(100, 200) - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter1.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter1.clone(), - &expected_results, - TestVariants::All, - ); - - let expected_results = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter2 = EdgeFilter - .window(100, 200) - .property("p1") - .eq("shivam_kapoor"); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter2.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_window_filter_any_all_over_window() { - let filter_any = EdgeFilter - .window(2, 4) - .property("p20") - .temporal() - .any() - .eq("Gold_boat"); - - let expected_any = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_any.clone(), - &expected_any, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_any.clone(), - &expected_any, - TestVariants::All, - ); - - let filter_all = EdgeFilter - .window(2, 4) - .property("p20") - .temporal() - .all() - .eq("Gold_boat"); - - let expected_all: Vec<&str> = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_all.clone(), - &expected_all, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_all.clone(), - &expected_all, - TestVariants::All, - ); - } - - #[test] - fn test_edges_window_filter_and() { - let filter1 = EdgeFilter - .window(3, 6) - .property("p10") - .temporal() - .any() - .eq("Paper_airplane"); - - let filter2 = EdgeFilter - .window(3, 6) - .property("p2") - .temporal() - .sum() - .eq(6u64); - - let filter = filter1.and(filter2); - - let expected_results = vec!["2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_layer_filter() { - let filter = EdgeFilter - .layer("fire_nation") - .property("p2") - .temporal() - .sum() - .ge(2u64); - - let expected_results = vec!["1->2", "3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_at_filter() { - // Only time=2 contributes; edge 2->3 has p2=2 at t=2 - let filter = EdgeFilter.at(2).property("p2").temporal().sum().eq(2u64); - - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - // Only time=3 contributes; edge 3->1 has p2=6 at t=3 - let filter = EdgeFilter.at(3).property("p2").temporal().sum().eq(6u64); - - let expected_results = vec!["3->1", "2->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_after_filter() { - // after(2) means t >= 3 - let filter = EdgeFilter.after(2).property("p2").temporal().sum().ge(6u64); - - // At t=3: 3->1 and 2->1 have p2=6 - // At t=4: David->John and John->Jimmy have p2=6 - let expected_results = vec![ - "3->1", - "2->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_before_filter() { - // before(3) means t <= 2 - let filter = EdgeFilter - .before(3) - .property("p2") - .temporal() - .sum() - .eq(2u64); - - // Only t=2 contributes for p2=2 -> 2->3 - let expected_results = vec!["2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - - // And p2=6 edges shouldn't match, because their p2=6 lives at t=3+. - let filter = EdgeFilter - .before(3) - .property("p2") - .temporal() - .sum() - .eq(6u64); - - let expected_results = vec![]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_latest_filter() { - // At latest time (currently t=4), only the t=4 edges exist in the Event graph. - // Use EventOnly so the expectation is stable and matches node-style. - let filter = EdgeFilter.latest().property("p2").eq(6u64); - - let expected_results = vec!["David Gilmour->John Mayer", "John Mayer->Jimmy Page"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_snapshot_at_semantics_event_graph() { - let t = 2; - - let filter_snapshot = EdgeFilter - .snapshot_at(t) - .property("p2") - .temporal() - .sum() - .eq(2u64); - - let filter_before = EdgeFilter - .before(t + 1) - .property("p2") - .temporal() - .sum() - .eq(2u64); - - let expected_results = vec!["2->3"]; - - // snapshot_at - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot, - &expected_results, - TestVariants::EventOnly, - ); - - // before(t+1) - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_before.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_before, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_snapshot_at_semantics_persistent_graph() { - let t = 2; - - let filter_snapshot = EdgeFilter - .snapshot_at(t) - .property("p2") - .temporal() - .sum() - .eq(2u64); - - let filter_at = EdgeFilter.at(t).property("p2").temporal().sum().eq(2u64); - - let expected_results = vec!["2->3"]; - - // snapshot_at - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot, - &expected_results, - TestVariants::PersistentOnly, - ); - - // at(t) - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_at.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_at, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_snapshot_latest_semantics_event_graph() { - let filter_snapshot_latest = EdgeFilter - .snapshot_latest() - .property("p2") - .temporal() - .sum() - .ge(6u64); - - let filter_noop = EdgeFilter.property("p2").temporal().sum().ge(6u64); - - // Across the whole event history, p2=6 appears at t=3 and t=4. - let expected_results = vec![ - "3->1", - "2->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - - // snapshot_latest - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot_latest.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot_latest, - &expected_results, - TestVariants::EventOnly, - ); - - // no-op baseline - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_noop.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_noop, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_snapshot_latest_semantics_persistent_graph() { - let filter_snapshot_latest = EdgeFilter.snapshot_latest().property("p2").eq(6u64); - - let filter_latest = EdgeFilter.latest().property("p2").eq(6u64); - - // In persistent latest state at t=4, these edges have p2=6: - // - t=3 edges: 3->1, 2->1 - // - t=4 edges: David->John, John->Jimmy - let expected_results = vec![ - "3->1", - "2->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - - // snapshot_latest - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot_latest.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_snapshot_latest, - &expected_results, - TestVariants::PersistentOnly, - ); - - // latest - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_latest.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter_latest, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_layer_then_window_ordering() { - // In layer "fire_nation" within window [1,3), edge 1->2 matches p1 == "shivam_kapoor". - let filter = EdgeFilter - .layer("fire_nation") - .window(1, 3) - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1->2"]; - - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_window_then_layer_ordering() { - // Same semantics, reversed chaining order. - let filter = EdgeFilter - .window(1, 3) - .layer("fire_nation") - .property("p1") - .eq("shivam_kapoor"); - - let expected_results = vec!["1->2"]; - - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_latest_layer() { - let filter = EdgeFilter - .latest() - .layer("fire_nation") - .property("p2") - .temporal() - .last() - .eq(7u64); - - let expected_results = vec![]; - - assert_filter_edges_results( - init_edges_graph2, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_layer_latest() { - let filter = EdgeFilter - .layer("fire_nation") - .latest() - .property("p2") - .temporal() - .last() - .eq(7u64); - - let expected_results = vec!["1->2"]; - - assert_filter_edges_results( - init_edges_graph2, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } - } - - #[cfg(test)] - mod test_edge_composite_filter { - use crate::test::{init_edges_graph, IdentityGraphTransformer}; - use raphtory::db::graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, - TestVariants, - }, - views::filter::model::{ - edge_filter::EdgeFilter, node_filter::ops::NodeFilterOps, - property_filter::ops::PropertyFilterOps, ComposableFilter, PropertyFilterFactory, - TryAsCompositeFilter, - }, - }; - - #[test] - fn test_filter_edge_for_src_dst() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter::src() - .name() - .eq("3") - .and(EdgeFilter::dst().name().eq("1")); - let expected_results = vec!["3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_unique_results_from_composite_filters() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter - .property("p2") - .ge(2u64) - .and(EdgeFilter.property("p2").ge(1u64)); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter - .property("p2") - .ge(2u64) - .or(EdgeFilter.property("p2").ge(5u64)); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_composite_filter_edges() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. - // TODO: Enable these test for event_disk_graph, persistent_disk_graph once string property is fixed. - let filter = EdgeFilter - .property("p2") - .eq(2u64) - .and(EdgeFilter.property("p1").eq("kapoor")); - let expected_results = Vec::<&str>::new(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .eq(2u64) - .or(EdgeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1->2", "2->3"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter.property("p1").eq("pometry").or(EdgeFilter - .property("p2") - .eq(6u64) - .and(EdgeFilter.property("p3").eq(1u64))); - let expected_results = vec![ - "2->1", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src() - .name() - .eq("13") - .and(EdgeFilter.property("p1").eq("prop1")); - let expected_results = Vec::<&str>::new(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter - .property("p2") - .eq(4u64) - .and(EdgeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src() - .name() - .eq("1") - .and(EdgeFilter.property("p1").eq("shivam_kapoor")); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::dst() - .name() - .eq("1") - .and(EdgeFilter.property("p2").eq(6u64)); - let expected_results = vec!["2->1", "3->1"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = EdgeFilter::src() - .name() - .eq("1") - .and(EdgeFilter.property("p1").eq("shivam_kapoor")) - .or(EdgeFilter.property("p3").eq(5u64)); - let expected_results = vec!["1->2"]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let filter = filter.try_as_composite_edge_filter().unwrap(); - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_not_composite_filter_edges() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for both filter_edges and search_edges. Search API uses filter API internally for this filter. - let filter = EdgeFilter::src() - .name() - .eq("13") - .and(EdgeFilter.property("p1").eq("prop1")) - .not(); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter::src() - .name() - .eq("13") - .and(EdgeFilter.property("p1").eq("prop1").not()) - .not(); - let expected_results = vec![ - "1->2", - "2->1", - "2->3", - "3->1", - "David Gilmour->John Mayer", - "John Mayer->Jimmy Page", - ]; - assert_filter_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - assert_search_edges_results( - init_edges_graph, - IdentityGraphTransformer, - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } - } -} diff --git a/raphtory/tests/test_layers.rs b/raphtory/tests/test_layers.rs deleted file mode 100644 index c9a9538d6f..0000000000 --- a/raphtory/tests/test_layers.rs +++ /dev/null @@ -1,787 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use proptest::proptest; - use raphtory::{ - db::graph::{graph::assert_graph_equal, views::deletion_graph::PersistentGraph}, - prelude::*, - test_storage, - test_utils::{build_graph, build_graph_layer, build_graph_strat, GraphFixture}, - }; - use raphtory_api::core::entities::GID; - use serde_json::json; - - #[test] - fn prop_test_layering() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, false), layer in proptest::sample::subsequence(&["_default", "a", "b"], 0..3))| { - let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); - let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); - assert_graph_equal(&g_layer, &g_layer_expected); - }) - } - - #[test] - fn test_node_explicit_node_additions() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{"10":{"props":{"t_props":[[0,[]]],"c_props":[]},"node_type":null}},"edges":[]})).unwrap(); - let layer = []; - let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); - let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); - - assert_graph_equal(&g_layer, &g_layer_expected); - } - - #[test] - fn test_failure() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[3,9,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[9,3,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let layer = ["_default", "b"]; - let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); - let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); - - assert_graph_equal(&g_layer, &g_layer_expected); - } - - #[test] - fn test_failure2() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let layer = ["_default", "b"]; - let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); - let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); - - assert_graph_equal(&g_layer, &g_layer_expected); - } - - #[test] - fn test_failure3() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{},"edges":[[[0,0,null],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,0,"b"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}],[[0,1,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let layer = ["_default", "b"]; - let g_layer_expected = Graph::from(build_graph_layer(&graph_f, &layer)); - let g = Graph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer.clone()); - - assert_graph_equal(&g_layer, &g_layer_expected); - } - - #[test] - fn prop_test_layering_persistent_graph() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), layer in proptest::sample::subsequence(&["_default", "a", "b"], 0..3))| { - let g_layer_expected = PersistentGraph::from(build_graph_layer(&graph_f, &layer)); - let g = PersistentGraph::from(build_graph(&graph_f)); - let g_layer = g.valid_layers(layer); - assert_graph_equal(&g_layer, &g_layer_expected); - }) - } - - #[test] - fn test_layer_node() { - let graph = Graph::new(); - - graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(0, 2, 3, NO_PROPS, Some("layer2")).unwrap(); - graph.add_edge(3, 2, 4, NO_PROPS, Some("layer1")).unwrap(); - graph.add_edge(1, 4, 1, NO_PROPS, Some("layer3")).unwrap(); - - test_storage!(&graph, |graph| { - let neighbours = graph - .layers(vec!["layer1", "layer2"]) - .unwrap() - .node(1) - .unwrap() - .neighbours() - .into_iter() - .collect_vec(); - assert_eq!( - neighbours[0] - .layers("layer2") - .unwrap() - .edges() - .id() - .collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - assert_eq!( - graph - .layers("layer2") - .unwrap() - .node(neighbours[0].name()) - .unwrap() - .edges() - .id() - .collect_vec(), - vec![(GID::U64(2), GID::U64(3))] - ); - let mut edges = graph - .layers("layer1") - .unwrap() - .node(neighbours[0].name()) - .unwrap() - .edges() - .id() - .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) - .collect_vec(); - edges.sort(); - assert_eq!(edges, vec![(1, 2), (2, 4)]); - let mut edges = graph - .layers("layer1") - .unwrap() - .edges() - .id() - .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) - .collect_vec(); - edges.sort(); - assert_eq!(edges, vec![(1, 2), (2, 4)]); - let mut edges = graph - .layers(vec!["layer1", "layer2"]) - .unwrap() - .edges() - .id() - .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) - .collect_vec(); - edges.sort(); - assert_eq!(edges, vec![(1, 2), (2, 3), (2, 4)]); - - let mut edges = graph - .layers(["layer1", "layer3"]) - .unwrap() - .window(0, 2) - .edges() - .id() - .filter_map(|(a, b)| a.to_u64().zip(b.to_u64())) - .collect_vec(); - edges.sort(); - assert_eq!(edges, vec![(1, 2), (4, 1)]); - }); - } - - #[test] - fn layering_tests() { - let graph = Graph::new(); - let e1 = graph.add_edge(0, 1, 2, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, Some("2")).unwrap(); - - println!("edge: {e1:?}"); - // FIXME: this is weird, see issue #1458 - assert!(e1.has_layer("2")); - let history = e1.layers("2").unwrap().history(); - println!("history: {:?}", history); - assert!(e1.layers("2").unwrap().history().is_empty()); - - test_storage!(&graph, |graph| { - let e = graph.edge(1, 2).unwrap(); - // layers with non-existing layers errors - assert!(e.layers(["1", "3"]).is_err()); - // valid_layers ignores non-existing layers - assert_eq!(e.valid_layers(["1", "3"]).layer_names(), ["1"]); - assert!(e.has_layer("1")); - assert!(e.has_layer("2")); - assert!(!e.has_layer("3")); - assert!(e.valid_layers("1").has_layer("1")); - assert!(!e.valid_layers("1").has_layer("2")); - }); - } - - pub mod test_filters_layer_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::GraphTransformer, - views::{layer_graph::LayeredGraph, window_graph::WindowedGraph}, - }, - }, - prelude::{LayerOps, TimeOps}, - }; - use std::ops::Range; - - struct LayeredGraphTransformer(Vec); - - impl GraphTransformer for LayeredGraphTransformer { - type Return = LayeredGraph; - fn apply(&self, graph: G) -> Self::Return { - graph.layers(self.0.clone()).unwrap() - } - } - - struct LayeredGraphWindowTransformer(Vec, Range); - - impl GraphTransformer for LayeredGraphWindowTransformer { - type Return = WindowedGraph>; - fn apply(&self, graph: G) -> Self::Return { - graph - .layers(self.0.clone()) - .unwrap() - .window(self.1.start, self.1.end) - } - } - - pub mod test_nodes_filters_layer_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::views::filter::model::property_filter::ops::PropertyFilterOps, - }, - prelude::AdditionOps, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - use crate::test::test_filters_layer_graph::{ - LayeredGraphTransformer, LayeredGraphWindowTransformer, - }; - use raphtory::{ - db::graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - TestGraphVariants, TestVariants, - }, - views::filter::model::PropertyFilterFactory, - }, - prelude::NodeFilter, - }; - - fn init_graph(graph: G) -> G { - let edges = vec![ - (6, "N1", "N2", vec![("p1", Prop::U64(2u64))], Some("layer1")), - (7, "N1", "N2", vec![("p1", Prop::U64(1u64))], Some("layer2")), - (6, "N2", "N3", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (7, "N2", "N3", vec![("p1", Prop::U64(2u64))], Some("layer2")), - (8, "N3", "N4", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (9, "N4", "N5", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (5, "N5", "N6", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (6, "N5", "N6", vec![("p1", Prop::U64(2u64))], Some("layer2")), - (5, "N6", "N7", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (6, "N6", "N7", vec![("p1", Prop::U64(1u64))], Some("layer2")), - (3, "N7", "N8", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (5, "N7", "N8", vec![("p1", Prop::U64(1u64))], Some("layer2")), - (3, "N8", "N1", vec![("p1", Prop::U64(1u64))], Some("layer1")), - (4, "N8", "N1", vec![("p1", Prop::U64(2u64))], Some("layer2")), - ]; - - for (id, src, tgt, props, layer) in &edges { - graph - .add_edge(*id, src, tgt, props.clone(), *layer) - .unwrap(); - } - - let nodes = vec![ - (6, "N1", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - (7, "N1", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (6, "N2", vec![("p1", Prop::U64(1u64))], Some("water_tribe")), - (7, "N2", vec![("p1", Prop::U64(2u64))], Some("water_tribe")), - (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (5, "N5", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (6, "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (6, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (3, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (4, "N8", vec![("p1", Prop::U64(2u64))], Some("fire_nation")), - ]; - - for (id, name, props, label) in &nodes { - graph - .add_node(*id, name, props.clone(), *label, None) - .unwrap(); - } - - graph - } - - // Layers don't have any effect on the number of nodes in a graph. - // In other words, it is as good as applying no layer filters. - #[test] - fn test_nodes_filters() { - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = NodeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2", "N5", "N8"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = NodeFilter.property("p1").lt(2u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = NodeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2", "N5", "N8"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_filters_w() { - // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = NodeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = NodeFilter.property("p1").lt(2u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_pg_w() { - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = NodeFilter.property("p1").lt(2u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = NodeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2", "N5", "N8"]; - assert_filter_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - - mod test_edges_filters_layer_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, - TestGraphVariants, TestVariants, - }, - views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, - }, - }, - prelude::{AdditionOps, EdgeFilter}, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - use crate::test::test_filters_layer_graph::{ - LayeredGraphTransformer, LayeredGraphWindowTransformer, - }; - - fn init_graph(graph: G) -> G { - let edges = vec![ - (6, "N1", "N2", 2u64, "layer1"), - (7, "N1", "N2", 1u64, "layer2"), - (6, "N2", "N3", 1u64, "layer1"), - (7, "N2", "N3", 2u64, "layer2"), - (8, "N3", "N4", 1u64, "layer1"), - (9, "N4", "N5", 1u64, "layer1"), - (5, "N5", "N6", 1u64, "layer1"), - (6, "N5", "N6", 2u64, "layer2"), - (5, "N6", "N7", 1u64, "layer1"), - (6, "N6", "N7", 1u64, "layer2"), - (3, "N7", "N8", 1u64, "layer1"), - (5, "N7", "N8", 1u64, "layer2"), - (3, "N8", "N1", 1u64, "layer1"), - (4, "N8", "N1", 2u64, "layer2"), - ]; - - for (ts, src, dst, p1_val, layer) in edges { - graph - .add_edge(ts, src, dst, [("p1", Prop::U64(p1_val))], Some(layer)) - .unwrap(); - } - - graph - } - - #[test] - fn test_edges_filters() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = vec![ - "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N1", - ]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = EdgeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = EdgeFilter.property("p1").lt(2u64); - let expected_results = vec![ - "N2->N3", "N3->N4", "N4->N5", "N5->N6", "N6->N7", "N7->N8", "N8->N1", - ]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = EdgeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; - assert_filter_edges_results( - init_graph, - LayeredGraphTransformer(layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphTransformer(layers), - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_filter_w() { - // Edge Property Semantics: - // 1. All property updates to an edge belong to a layer (or _default if no layer specified) - // 2. However, when asked for a value of a particular property for an edge, the latest update - // across all specified layers (or all layers if no layers specified) is returned! - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - // Edge Property Semantics: - // When filtering by specific layer, filter criteria (p1==1) and latest semantics is applicable - // only to that specific layer. - let layers: Vec = vec!["layer1".into()]; - let filter = EdgeFilter.property("p1").lt(2u64); - let expected_results = vec!["N2->N3", "N3->N4"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = EdgeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_w() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph. - let layers: Vec = vec!["layer1".into(), "layer2".into()]; - let filter = EdgeFilter.property("p1").eq(1u64); - - // Why is the edge N8 -> N1 included in the results? - // The reason edge N8 -> N1 is included as part of the results because of following two semantic reasons: - // .add_edge(3, "N8", "N1", [("p1", Prop::U64(1u64))], Some("layer1")) - // .add_edge(4, "N8", "N1", [("p1", Prop::U64(2u64))], Some("layer2")) - // 1. As per layer graph semantics, every edge update belongs to a particular layer (or '_default' if no layer specified). - // This means the last_before is computed per layer and not across layers. In other words, when computing - // last_before for (N8->N1, layer1) and window(6, 9), t = 3 is the correct last before edge update timestamp and not t = 4 - // because t=4 edge update is in layer2. - // 2. Since the search is conducted across both the layers i.e., layer1 and layer2, the results are union of - // results from both layer1 and layer2. - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - - let layers: Vec = vec!["layer1".into()]; - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = - vec!["N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N1"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - - let layers: Vec = vec!["layer2".into()]; - let filter = EdgeFilter.property("p1").ge(2u64); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N1"]; - assert_filter_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - assert_search_edges_results( - init_graph, - LayeredGraphWindowTransformer(layers, 6..9), - filter, - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - } - } - } -} diff --git a/raphtory/tests/test_materialize.rs b/raphtory/tests/test_materialize.rs deleted file mode 100644 index 99ffa62d36..0000000000 --- a/raphtory/tests/test_materialize.rs +++ /dev/null @@ -1,193 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use proptest::{arbitrary::any, proptest}; - use raphtory::{ - db::graph::graph::{assert_graph_equal, assert_graph_equal_timestamps}, - prelude::*, - test_storage, - test_utils::{build_edge_list, build_graph_from_edge_list}, - }; - use raphtory_api::core::storage::arc_str::OptionAsStr; - use raphtory_storage::core_ops::CoreGraphOps; - use std::ops::Range; - - #[test] - fn test_materialize() { - let g = Graph::new(); - g.add_edge(0, 1, 2, [("layer1", "1")], Some("1")).unwrap(); - g.add_edge(0, 1, 2, [("layer2", "2")], Some("2")).unwrap(); - - let gm = g.materialize().unwrap(); - - assert_graph_equal(&g, &gm); - assert_eq!( - gm.nodes().name().iter_values().collect::>(), - vec!["1", "2"] - ); - - assert!(g - .layers("2") - .unwrap() - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("layer1") - .and_then(|prop| prop.latest()) - .is_none()); - - assert!(gm - .into_events() - .unwrap() - .layers("2") - .unwrap() - .edge(1, 2) - .unwrap() - .properties() - .temporal() - .get("layer1") - .and_then(|prop| prop.latest()) - .is_none()); - } - - #[test] - fn test_graph_properties() { - let g = Graph::new(); - g.add_properties(1, [("test", "test")]).unwrap(); - g.add_metadata([("test_metadata", "test2")]).unwrap(); - - test_storage!(&g, |g| { - let gm = g.materialize().unwrap(); - assert_graph_equal(&g, &gm); - }); - } - - #[test] - fn materialize_prop_test() { - proptest!(|(edges in build_edge_list(100, 100), w in any::>())| { - let g = build_graph_from_edge_list(&edges); - test_storage!(&g, |g| { - let gm = g.materialize().unwrap(); - assert_graph_equal_timestamps(&g, &gm); - let gw = g.window(w.start, w.end); - let gmw = gw.materialize().unwrap(); - assert_graph_equal_timestamps(&gw, &gmw); - }); - }) - } - - #[test] - fn test_subgraph() { - let g = Graph::new(); - g.add_node(0, 1, NO_PROPS, None, None).unwrap(); - g.add_node(0, 2, NO_PROPS, None, None).unwrap(); - g.add_node(0, 3, NO_PROPS, None, None).unwrap(); - g.add_node(0, 4, NO_PROPS, None, None).unwrap(); - g.add_node(0, 5, NO_PROPS, None, None).unwrap(); - - let nodes_subgraph = g.subgraph(vec![4, 5]); - assert_eq!( - nodes_subgraph - .nodes() - .name() - .iter_values() - .collect::>(), - vec!["4", "5"] - ); - let gm = nodes_subgraph.materialize().unwrap(); - assert_graph_equal(&nodes_subgraph, &gm); - } - - #[test] - fn test_exclude_nodes() { - let g = Graph::new(); - g.add_node(0, 1, NO_PROPS, None, None).unwrap(); - g.add_node(0, 2, NO_PROPS, None, None).unwrap(); - g.add_node(0, 3, NO_PROPS, None, None).unwrap(); - g.add_node(0, 4, NO_PROPS, None, None).unwrap(); - g.add_node(0, 5, NO_PROPS, None, None).unwrap(); - - let exclude_nodes_subgraph = g.exclude_nodes(vec![4, 5]); - assert_eq!( - exclude_nodes_subgraph - .nodes() - .name() - .iter_values() - .sorted() - .collect::>(), - vec!["1", "2", "3"] - ); - let gm = exclude_nodes_subgraph.materialize().unwrap(); - assert_graph_equal(&exclude_nodes_subgraph, &gm); - } - - #[test] - fn testing_node_types() { - let graph = Graph::new(); - graph.add_node(0, "A", NO_PROPS, None, None).unwrap(); - graph.add_node(1, "B", NO_PROPS, Some("H"), None).unwrap(); - - test_storage!(&graph, |graph| { - let node_a = graph.node("A").unwrap(); - let node_b = graph.node("B").unwrap(); - let node_a_type = node_a.node_type(); - let node_a_type_str = node_a_type.as_str(); - - assert_eq!(node_a_type_str, None); - assert_eq!(node_b.node_type().as_str(), Some("H")); - }); - - // Nodes with No type can be overwritten - let node_a = graph - .add_node(1, "A", NO_PROPS, Some("TYPEA"), None) - .unwrap(); - assert_eq!(node_a.node_type().as_str(), Some("TYPEA")); - - // Check that overwriting a node type returns an error - assert!(graph - .add_node(2, "A", NO_PROPS, Some("TYPEB"), None) - .is_err()); - // Double check that the type did not actually change - assert_eq!(graph.node("A").unwrap().node_type().as_str(), Some("TYPEA")); - // Check that the update is not added to the graph - let all_node_types = graph.get_all_node_types(); - assert_eq!(all_node_types.len(), 2); - } - - #[test] - fn changing_property_type_errors() { - let g = Graph::new(); - let props_0 = [("test", Prop::U64(1))]; - let props_1 = [("test", Prop::F64(0.1))]; - g.add_properties(0, props_0.clone()).unwrap(); - assert!(g.add_properties(1, props_1.clone()).is_err()); - - g.add_node(0, 1, props_0.clone(), None, None).unwrap(); - assert!(g.add_node(1, 1, props_1.clone(), None, None).is_err()); - - g.add_edge(0, 1, 2, props_0.clone(), None).unwrap(); - assert!(g.add_edge(1, 1, 2, props_1.clone(), None).is_err()); - } - - #[test] - fn test_edge_layer_properties() { - let g = Graph::new(); - g.add_edge(1, "A", "B", [("greeting", "howdy")], Some("layer 1")) - .unwrap(); - g.add_edge(2, "A", "B", [("greeting", "ola")], Some("layer 2")) - .unwrap(); - g.add_edge(2, "A", "B", [("greeting", "hello")], Some("layer 2")) - .unwrap(); - g.add_edge(3, "A", "B", [("greeting", "namaste")], Some("layer 3")) - .unwrap(); - - let edge_ab = g.edge("A", "B").unwrap(); - let props = edge_ab - .properties() - .iter() - .filter_map(|(k, v)| v.map(move |v| (k.to_string(), v.to_string()))) - .collect::>(); - assert_eq!(props, vec![("greeting".to_string(), "namaste".to_string())]); - } -} diff --git a/raphtory/tests/tests_node_type_filtered_subgraph.rs b/raphtory/tests/tests_node_type_filtered_subgraph.rs deleted file mode 100644 index 8531670dea..0000000000 --- a/raphtory/tests/tests_node_type_filtered_subgraph.rs +++ /dev/null @@ -1,697 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use proptest::{arbitrary::any, proptest}; - use raphtory::{ - db::{ - api::view::Filter, - graph::{ - graph::assert_graph_equal, - views::filter::model::{ - property_filter::ops::PropertyFilterOps, PropertyFilterFactory, - }, - }, - }, - prelude::*, - test_utils::{build_graph, build_graph_strat, make_node_types, GraphFixture}, - }; - use raphtory_storage::mutation::addition_ops::InternalAdditionOps; - use serde_json::json; - use std::ops::Range; - - #[test] - fn test_type_filtered_subgraph() { - let graph = Graph::new(); - let edges = vec![ - (1, "A", "B", vec![("p1", 1u64)], None), - (2, "B", "C", vec![("p1", 2u64)], None), - (3, "C", "D", vec![("p1", 3u64)], None), - (4, "D", "E", vec![("p1", 4u64)], None), - ]; - - for (id, src, dst, props, layer) in &edges { - graph - .add_edge(*id, src, dst, props.clone(), *layer) - .unwrap(); - } - - let nodes = vec![ - (1, "A", vec![("p1", 1u64)], Some("water_tribe")), - (2, "B", vec![("p1", 2u64)], Some("water_tribe")), - (3, "C", vec![("p1", 1u64)], Some("fire_nation")), - (4, "D", vec![("p1", 1u64)], Some("air_nomads")), - ]; - - for (id, name, props, layer) in &nodes { - graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); - } - - let filtered_graph = graph - .subgraph_node_types(vec!["fire_nation", "air_nomads"]) - .window(1, 5); - - assert_eq!(filtered_graph.nodes(), vec!["C", "D"]); - - assert_eq!( - filtered_graph - .filter(NodeFilter.property("p1").eq(1u64)) - .unwrap() - .nodes(), - vec!["C", "D"] - ); - - assert!(filtered_graph - .filter(EdgeFilter.property("p1").eq(1u64)) - .unwrap() - .edges() - .is_empty()) - } - - #[test] - fn materialize_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), node_types in make_node_types())| { - let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); - let gm = g.materialize().unwrap(); - assert_graph_equal(&g, &gm); - }) - } - - #[test] - fn materialize_type_window_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), node_types in make_node_types())| { - let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); - let gvw = g.window(w.start, w.end); - let gmw = gvw.materialize().unwrap(); - assert_graph_equal(&gvw, &gmw); - }) - } - - #[test] - fn materialize_type_window_prop_test_failure() { - let graph_f: GraphFixture = serde_json::from_value(json!({"nodes":{"9":{"props":{"t_props":[[1,[]]],"c_props":[]},"node_type":"one"},"8":{"props":{"t_props":[[1,[]]],"c_props":[]},"node_type":"one"}},"edges":[[[8,8,"a"],{"props":{"t_props":[[0,[]]],"c_props":[]},"deletions":[]}]]})).unwrap(); - let w = 1..2; - let node_types = ["one"]; - let g = Graph::from(build_graph(&graph_f)).subgraph_node_types(node_types); - let gvw = g.window(w.start, w.end); - assert_eq!(gvw.node("8").unwrap().out_degree(), 0); // edge is not in the window - let gmw = gvw.materialize().unwrap(); - assert_graph_equal(&gvw, &gmw); - } - - #[test] - fn materialize_window_type_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>(), node_types in make_node_types())| { - let g = Graph::from(build_graph(&graph_f)); - let gvw = g.window(w.start, w.end).subgraph_node_types(node_types); - let gmw = gvw.materialize().unwrap(); - assert_graph_equal(&gvw, &gmw); - }) - } - - #[test] - fn node_removed_via_edge_removal() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.node(1).unwrap().set_node_type("test").unwrap(); - let expected = Graph::new(); - expected.resolve_layer(None).unwrap(); - assert_graph_equal(&g.subgraph_node_types(["test"]), &expected); - } - - #[test] - fn node_removed_via_edge_removal_window() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.node(0).unwrap().set_node_type("two").unwrap(); - let gw = g.window(0, 1); - let expected = Graph::new(); - expected.resolve_layer(None).unwrap(); - let sg = gw.subgraph_node_types(["_default"]); - assert!(!sg.has_node(0)); - assert!(!sg.has_node(1)); - assert_graph_equal(&sg, &expected); - assert_graph_equal(&sg, &sg.materialize().unwrap()) - } - mod test_filters_node_type_filtered_subgraph { - use raphtory::{ - db::{ - api::{state::ops::filter::NodeTypeFilterOp, view::StaticGraphViewOps}, - graph::{ - assertions::GraphTransformer, - views::{ - filter::node_filtered_graph::NodeFilteredGraph, layer_graph::LayeredGraph, - window_graph::WindowedGraph, - }, - }, - }, - prelude::{GraphViewOps, LayerOps, NodeViewOps, TimeOps}, - }; - use std::ops::Range; - - fn get_all_node_types(graph: &G) -> Vec { - graph - .nodes() - .node_type() - .into_iter() - .flat_map(|(_, node_type)| node_type) - .map(|s| s.to_string()) - .collect() - } - - struct NodeTypeGraphTransformer(Option>); - - impl GraphTransformer for NodeTypeGraphTransformer { - type Return = NodeFilteredGraph; - fn apply(&self, graph: G) -> Self::Return { - let node_types: Vec = - self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); - graph.subgraph_node_types(node_types) - } - } - - struct WindowedNodeTypeGraphTransformer(Option>, Range); - - impl GraphTransformer for WindowedNodeTypeGraphTransformer { - type Return = - WindowedGraph>; - fn apply(&self, graph: G) -> Self::Return { - let node_types: Vec = - self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); - graph - .subgraph_node_types(node_types) - .window(self.1.start, self.1.end) - } - } - - struct LayeredNodeTypeGraphTransformer(Option>, Vec); - - impl GraphTransformer for LayeredNodeTypeGraphTransformer { - type Return = - LayeredGraph>; - fn apply(&self, graph: G) -> Self::Return { - let node_types: Vec = - self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); - graph - .subgraph_node_types(node_types) - .layers(self.1.clone()) - .unwrap() - } - } - - struct LayeredWindowedNodeTypeGraphTransformer( - Option>, - Range, - Vec, - ); - - impl GraphTransformer for LayeredWindowedNodeTypeGraphTransformer { - type Return = - WindowedGraph>>; - fn apply(&self, graph: G) -> Self::Return { - let node_types: Vec = - self.0.clone().unwrap_or_else(|| get_all_node_types(&graph)); - graph - .subgraph_node_types(node_types) - .layers(self.2.clone()) - .unwrap() - .window(self.1.start, self.1.end) - } - } - - mod test_nodes_filters_node_type_filtered_subgraph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::views::filter::model::property_filter::ops::PropertyFilterOps, - }, - prelude::AdditionOps, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - fn init_graph(graph: G) -> G { - let nodes = vec![ - (6, "N1", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - (7, "N1", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (6, "N2", vec![("p1", Prop::U64(1u64))], Some("water_tribe")), - (7, "N2", vec![("p1", Prop::U64(2u64))], Some("water_tribe")), - (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (5, "N5", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (6, "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (6, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (3, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - (4, "N8", vec![("p1", Prop::U64(2u64))], Some("fire_nation")), - ]; - - // Add nodes to the graph - for (id, name, props, layer) in &nodes { - graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); - } - - graph - } - - use crate::test::test_filters_node_type_filtered_subgraph::{ - NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, - }; - use raphtory::{ - db::graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - TestGraphVariants, TestVariants, - }, - views::filter::model::PropertyFilterFactory, - }, - prelude::NodeFilter, - }; - #[test] - fn test_nodes_filters() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - NodeTypeGraphTransformer(None), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - NodeTypeGraphTransformer(None), - filter, - &expected_results, - TestVariants::All, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N4", "N7"]; - assert_filter_nodes_results( - init_graph, - NodeTypeGraphTransformer(node_types.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_nodes_results( - init_graph, - NodeTypeGraphTransformer(node_types), - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_nodes_filters_w() { - // TODO: Enable event_disk_graph for filter_nodes once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_pg_w() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types, 6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - - mod test_edges_filters_node_type_filtered_subgraph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::views::filter::model::property_filter::ops::PropertyFilterOps, - }, - prelude::{AdditionOps, NO_PROPS}, - }; - use raphtory_api::core::entities::properties::prop::Prop; - - fn init_graph(graph: G) -> G { - let edges = vec![ - ( - 6, - "N1", - "N2", - vec![("p1", Prop::U64(2u64))], - Some("fire_nation"), - ), - (7, "N1", "N2", vec![("p1", Prop::U64(1u64))], None), - ( - 6, - "N2", - "N3", - vec![("p1", Prop::U64(1u64))], - Some("water_tribe"), - ), - ( - 7, - "N2", - "N3", - vec![("p1", Prop::U64(2u64))], - Some("water_tribe"), - ), - ( - 8, - "N3", - "N4", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - (9, "N4", "N5", vec![("p1", Prop::U64(1u64))], None), - ( - 5, - "N5", - "N6", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - (6, "N5", "N6", vec![("p1", Prop::U64(2u64))], None), - ( - 5, - "N6", - "N7", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - ( - 6, - "N6", - "N7", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - ( - 3, - "N7", - "N8", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - (5, "N7", "N8", vec![("p1", Prop::U64(1u64))], None), - ( - 3, - "N8", - "N1", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - ( - 4, - "N8", - "N1", - vec![("p1", Prop::U64(2u64))], - Some("water_tribe"), - ), - ]; - - for (id, src, dst, props, layer) in &edges { - graph - .add_edge(*id, src, dst, props.clone(), *layer) - .unwrap(); - } - - let nodes = vec![ - (6, "N1", NO_PROPS, Some("air_nomad")), - (6, "N2", NO_PROPS, Some("water_tribe")), - (8, "N3", NO_PROPS, Some("air_nomad")), - (9, "N4", NO_PROPS, Some("air_nomad")), - (5, "N5", NO_PROPS, Some("air_nomad")), - (5, "N6", NO_PROPS, Some("fire_nation")), - (3, "N7", NO_PROPS, Some("air_nomad")), - (4, "N8", NO_PROPS, Some("fire_nation")), - ]; - - for (id, name, props, layer) in &nodes { - graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); - } - - graph - } - - use crate::test::test_filters_node_type_filtered_subgraph::{ - LayeredNodeTypeGraphTransformer, LayeredWindowedNodeTypeGraphTransformer, - NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, - }; - use raphtory::{ - db::graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, TestVariants, - }, - views::filter::model::PropertyFilterFactory, - }, - prelude::EdgeFilter, - }; - - #[test] - fn test_edges_filters() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - NodeTypeGraphTransformer(None), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - NodeTypeGraphTransformer(None), - filter, - &expected_results, - TestVariants::All, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N4->N5"]; - assert_filter_edges_results( - init_graph, - NodeTypeGraphTransformer(node_types.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - NodeTypeGraphTransformer(node_types.clone()), - filter.clone(), - &expected_results, - TestVariants::All, - ); - - let layers = vec!["fire_nation".to_string()]; - let expected_results = vec!["N3->N4"]; - assert_filter_edges_results( - init_graph, - LayeredNodeTypeGraphTransformer(node_types.clone(), layers.clone()), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredNodeTypeGraphTransformer(node_types.clone(), layers), - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_filters_w() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4"]; - assert_filter_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let layers = vec!["fire_nation".to_string()]; - let expected_results = vec!["N3->N4"]; - assert_filter_edges_results( - init_graph, - LayeredWindowedNodeTypeGraphTransformer( - node_types.clone(), - 6..9, - layers.clone(), - ), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_w() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(None, 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let node_types: Option> = - Some(vec!["air_nomad".into(), "water_tribe".into()]); - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4"]; - assert_filter_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let layers = vec!["fire_nation".to_string()]; - let expected_results = vec!["N3->N4"]; - assert_filter_edges_results( - init_graph, - LayeredWindowedNodeTypeGraphTransformer( - node_types.clone(), - 6..9, - layers.clone(), - ), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - LayeredWindowedNodeTypeGraphTransformer(node_types.clone(), 6..9, layers), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - } -} diff --git a/raphtory/tests/time_tests.rs b/raphtory/tests/time_tests.rs deleted file mode 100644 index 046076177a..0000000000 --- a/raphtory/tests/time_tests.rs +++ /dev/null @@ -1,336 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use raphtory::{ - db::{ - api::{mutation::AdditionOps, view::WindowSet}, - graph::{ - graph::{assert_graph_equal, Graph}, - views::deletion_graph::PersistentGraph, - }, - }, - prelude::{DeletionOps, GraphViewOps, LayerOps, TimeOps, NO_PROPS}, - test_storage, - }; - use raphtory_api::core::{ - storage::timeindex::AsTime, - utils::time::{ParseTimeError, TryIntoTime}, - }; - - // start inclusive, end exclusive - fn graph_with_timeline(start: i64, end: i64) -> Graph { - let g = Graph::new(); - g.add_edge(start, 0, 1, NO_PROPS, None).unwrap(); - g.add_edge(end - 1, 0, 1, NO_PROPS, None).unwrap(); - g - } - - fn assert_bounds<'graph, G>( - windows: WindowSet<'graph, G>, - expected: &[(Option, Option)], - ) where - G: GraphViewOps<'graph>, - { - let window_bounds = windows - .map(|w| (w.start().map(|t| t.t()), w.end().map(|t| t.t()))) - .collect_vec(); - assert_eq!(window_bounds, expected) - } - - #[test] - fn snapshot() { - let graph = PersistentGraph::new(); - graph.add_edge(3, 0, 1, [("a", "a")], None).unwrap(); - graph.add_edge(4, 0, 2, [("b", "b")], None).unwrap(); - graph.delete_edge(5, 0, 1, None).unwrap(); - - for time in 2..7 { - assert_graph_equal(&graph.at(time), &graph.snapshot_at(time)); - } - assert_graph_equal(&graph.latest(), &graph.snapshot_latest()); - - let graph = graph.event_graph(); - - for time in 2..7 { - assert_graph_equal(&graph.before(time + 1), &graph.snapshot_at(time)); - } - assert_graph_equal(&graph, &graph.snapshot_latest()); - } - - #[test] - fn rolling() { - let graph = graph_with_timeline(1, 7); - test_storage!(&graph, |graph| { - let windows = graph.rolling(2, None).unwrap(); - let expected = vec![(Some(1), Some(3)), (Some(3), Some(5)), (Some(5), Some(7))]; - assert_bounds(windows, &expected); - }); - - let graph = graph_with_timeline(1, 6); - test_storage!(&graph, |graph| { - let windows = graph.rolling(3, Some(2)).unwrap(); - let expected = vec![(Some(0), Some(3)), (Some(2), Some(5)), (Some(4), Some(7))]; - assert_bounds(windows, &expected); - }); - - let graph = graph_with_timeline(0, 9); - test_storage!(&graph, |graph| { - let windows = graph.window(1, 6).rolling(3, Some(2)).unwrap(); - assert_bounds( - windows, - &[(Some(1), Some(3)), (Some(2), Some(5)), (Some(4), Some(6))], - ); - }); - } - - #[test] - fn rolling_layered() { - let graph = Graph::new(); - graph.add_edge(1, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(5, 0, 1, NO_PROPS, Some("1")).unwrap(); - - graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); - graph.add_edge(6, 0, 1, NO_PROPS, Some("2")).unwrap(); - - test_storage!(&graph, |graph| { - let windows = graph.rolling(2, None).unwrap(); - let expected = vec![(Some(1), Some(3)), (Some(3), Some(5)), (Some(5), Some(7))]; - assert_bounds(windows, &expected); - - let windows = graph.layers("1").unwrap().rolling(2, None).unwrap(); - assert_bounds(windows, &expected); - - let windows = graph.layers("2").unwrap().rolling(2, None).unwrap(); - assert_bounds(windows, &expected); - }); - - let graph = Graph::new(); - graph.add_edge(1, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(3, 0, 1, NO_PROPS, Some("1")).unwrap(); - - graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); - graph.add_edge(5, 0, 1, NO_PROPS, Some("2")).unwrap(); - test_storage!(&graph, |graph| { - let windows = graph.rolling(3, Some(2)).unwrap(); - let expected = vec![(Some(0), Some(3)), (Some(2), Some(5)), (Some(4), Some(7))]; - assert_bounds(windows, &expected); - - let windows = graph.layers("1").unwrap().rolling(3, Some(2)).unwrap(); - assert_bounds(windows, &expected); - - let windows = graph.layers("2").unwrap().rolling(3, Some(2)).unwrap(); - assert_bounds(windows, &expected); - }); - - let graph = Graph::new(); - graph.add_edge(0, 0, 1, NO_PROPS, Some("1")).unwrap(); - graph.add_edge(5, 0, 1, NO_PROPS, Some("1")).unwrap(); - - graph.add_edge(3, 0, 1, NO_PROPS, Some("2")).unwrap(); - graph.add_edge(8, 0, 1, NO_PROPS, Some("2")).unwrap(); - test_storage!(&graph, |graph| { - let windows = graph.window(1, 6).rolling(3, Some(2)).unwrap(); - let expected = [(Some(1), Some(3)), (Some(2), Some(5)), (Some(4), Some(6))]; - assert_bounds(windows, &expected); - - let windows = graph - .layers("1") - .unwrap() - .window(1, 6) - .rolling(3, Some(2)) - .unwrap(); - assert_bounds(windows, &expected); - - let windows = graph - .layers("2") - .unwrap() - .window(1, 6) - .rolling(3, Some(2)) - .unwrap(); - assert_bounds(windows, &expected); - }); - } - - #[test] - fn expanding() { - let graph = graph_with_timeline(1, 7); - test_storage!(&graph, |graph| { - let windows = graph.expanding(2).unwrap(); - let expected = vec![(None, Some(3)), (None, Some(5)), (None, Some(7))]; - assert_bounds(windows, &expected); - }); - - let graph = graph_with_timeline(1, 6); - test_storage!(&graph, |graph| { - let windows = graph.expanding(2).unwrap(); - let expected = vec![(None, Some(3)), (None, Some(5)), (None, Some(7))]; - assert_bounds(windows, &expected); - }); - - let graph = graph_with_timeline(0, 9); - test_storage!(&graph, |graph| { - let windows = graph.window(1, 6).expanding(2).unwrap(); - assert_bounds( - windows, - &[(Some(1), Some(3)), (Some(1), Some(5)), (Some(1), Some(6))], - ); - }); - } - - #[test] - fn rolling_dates() { - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - test_storage!(&graph, |graph| { - let windows = graph.rolling("1 day", None).unwrap(); - let expected = vec![ - ( - "2020-06-06 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ( - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 - "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ]; - assert_bounds(windows, &expected); - }); - - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-08 00:00:00".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - test_storage!(&graph, |graph| { - let windows = graph.rolling("1 day", None).unwrap(); - let expected = vec![ - ( - "2020-06-06 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-06 - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ( - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), // entire 2020-06-07 - "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ]; - assert_bounds(windows, &expected); - }); - - // TODO: turn this back on if we bring bach epoch alignment for unwindowed graphs - // let start = "2020-06-05 23:59:59.999".into_time().unwrap(); - // let end = "2020-06-07 00:00:00.000".into_time().unwrap(); - // let g = graph_with_timeline(start, end); - // let windows = g.rolling("1 day", None).unwrap(); - // let expected = vec![ - // ( - // "2020-06-05 00:00:00".into_time().unwrap(), // entire 2020-06-06 - // "2020-06-06 00:00:00".into_time().unwrap(), - // ), - // ( - // "2020-06-06 00:00:00".into_time().unwrap(), // entire 2020-06-07 - // "2020-06-07 00:00:00".into_time().unwrap(), - // ), - // ]; - // assert_bounds(windows, expected); - } - - #[test] - fn test_errors() { - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - match graph.rolling("1 day", Some("0 days")) { - Ok(_) => { - panic!("Expected error, but got Ok") - } - Err(e) => { - assert_eq!(e, ParseTimeError::ZeroSizeStep) - } - } - match graph.rolling(1, Some(0)) { - Ok(_) => { - panic!("Expected error, but got Ok") - } - Err(e) => { - assert_eq!(e, ParseTimeError::ZeroSizeStep) - } - } - match graph.expanding("0 day") { - Ok(_) => { - panic!("Expected error, but got Ok") - } - Err(e) => { - assert_eq!(e, ParseTimeError::ZeroSizeStep) - } - } - match graph.expanding(0) { - Ok(_) => { - panic!("Expected error, but got Ok") - } - Err(e) => { - assert_eq!(e, ParseTimeError::ZeroSizeStep) - } - } - - match graph.expanding("0fead day") { - Ok(_) => { - panic!("Expected error, but got Ok") - } - Err(e) => { - assert!(matches!(e, ParseTimeError::ParseInt { .. })) - } - } - - match graph.expanding("0 dadasasdy") { - Ok(_) => { - panic!("Expected error, but got Ok") - } - Err(e) => { - assert!(matches!(e, ParseTimeError::InvalidUnit { .. })) - } - } - - assert_eq!( - graph.rolling("1 day", Some("1000 days")).unwrap().count(), - 0 - ) - } - - #[test] - fn expanding_dates() { - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-07 23:59:59.999".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - test_storage!(&graph, |graph| { - let windows = graph.expanding("1 day").unwrap(); - let expected = vec![ - ( - None, - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ( - None, - "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ]; - assert_bounds(windows, &expected); - }); - - let start = "2020-06-06 00:00:00".try_into_time().unwrap().t(); - let end = "2020-06-08 00:00:00".try_into_time().unwrap().t(); - let graph = graph_with_timeline(start, end); - test_storage!(&graph, |graph| { - let windows = graph.expanding("1 day").unwrap(); - let expected = vec![ - ( - None, - "2020-06-07 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ( - None, - "2020-06-08 00:00:00".try_into_time().ok().map(|t| t.t()), - ), - ]; - assert_bounds(windows, &expected); - }); - } -} diff --git a/raphtory/tests/valid_graph.rs b/raphtory/tests/valid_graph.rs deleted file mode 100644 index d34955c081..0000000000 --- a/raphtory/tests/valid_graph.rs +++ /dev/null @@ -1,382 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use proptest::{arbitrary::any, proptest}; - use raphtory::{ - db::graph::{ - graph::{assert_graph_equal, assert_persistent_materialize_graph_equal}, - views::deletion_graph::PersistentGraph, - }, - errors::GraphError, - prelude::*, - test_utils::{build_graph, build_graph_strat}, - }; - use raphtory_api::core::storage::timeindex::AsTime; - use raphtory_storage::mutation::addition_ops::InternalAdditionOps; - use std::ops::Range; - - #[test] - fn test_valid_graph_persistent() -> Result<(), GraphError> { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None)?; - g.delete_edge(10, 0, 1, None)?; - - assert_eq!( - g.window(0, 2).valid().edges().id().collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); - assert!(g.valid().edges().is_empty()); - Ok(()) - } - - #[test] - fn test_valid_graph_events() -> Result<(), GraphError> { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None)?; - g.delete_edge(10, 0, 1, None)?; - - assert_eq!( - g.window(0, 2).valid().edges().id().collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); - assert!(g.window(1, 20).valid().edges().is_empty()); // only deletion event in window, not valid - assert_eq!( - g.valid().edges().id().collect_vec(), - [(GID::U64(0), GID::U64(1))] - ); - Ok(()) - } - - #[test] - fn materialize_prop_test_persistent() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { - let g = PersistentGraph::from(build_graph(&graph_f)).valid(); - let gm = g.materialize().unwrap(); - assert_graph_equal(&g, &gm); - }) - } - - #[test] - fn test_explode_layers() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, Some("a")).unwrap(); - g.delete_edge(0, 0, 1, Some("b")).unwrap(); - let gv = g.valid(); - let edge = gv.edge(0, 1).unwrap(); - let exploded = edge.explode_layers(); - let layers = exploded - .layer_name() - .collect::, _>>() - .unwrap(); - assert_eq!(layers, ["a"]); - } - - #[test] - fn materialize_prop_test_events() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true))| { - let g = Graph::from(build_graph(&graph_f)).valid(); - let gm = g.materialize().unwrap(); - assert_graph_equal(&g, &gm); - }) - } - - #[test] - fn test_materialize_self_loop() { - let g = Graph::new(); - g.add_edge(0, 0, 0, NO_PROPS, Some("a")).unwrap(); - let gm = g.valid().materialize().unwrap(); - assert_graph_equal(&g, &gm); - } - - #[test] - fn test_single_deleted_edge_events() { - let g = Graph::new(); - g.delete_edge(0, 0, 0, Some("a")).unwrap(); - let gv = g.valid(); - assert_eq!(gv.count_nodes(), 0); - assert_eq!(gv.count_edges(), 0); - assert_eq!(gv.count_temporal_edges(), 0); - - assert_eq!(gv.valid_layers("a").count_nodes(), 0); - - let expected = Graph::new(); - expected.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gv, &expected); - let gvm = gv.materialize().unwrap(); - assert_graph_equal(&gv, &gvm); - } - - #[test] - fn test_single_deleted_edge_persistent() { - let g = PersistentGraph::new(); - g.delete_edge(0, 0, 0, Some("a")).unwrap(); - let gv = g.valid(); - assert_eq!(gv.count_nodes(), 0); - assert_eq!(gv.count_edges(), 0); - assert_eq!(gv.count_temporal_edges(), 0); - - let expected = PersistentGraph::new(); - expected.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gv, &expected); - let gvm = gv.materialize().unwrap(); - assert_graph_equal(&gv, &gvm); - } - - #[test] - fn materialize_valid_window_persistent_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = PersistentGraph::from(build_graph(&graph_f)); - let gvw = g.valid().window(w.start, w.end); - let gmw = gvw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gvw, &gmw); - }) - } - - #[test] - fn test_deletions_in_window_but_edge_valid() { - let g = PersistentGraph::new(); - g.delete_edge(0, 0, 0, None).unwrap(); - g.delete_edge(0, 0, 1, None).unwrap(); - g.add_edge(5, 0, 1, NO_PROPS, None).unwrap(); - let gvw = g.valid().window(-1, 1); - assert_eq!(gvw.node(0).unwrap().out_degree(), 1); - } - - #[test] - fn materialize_valid_window_events_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = Graph::from(build_graph(&graph_f)); - let gvw = g.valid().window(w.start, w.end); - let gmw = gvw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gvw, &gmw); - }) - } - - #[test] - fn materialize_window_valid_persistent_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = PersistentGraph::from(build_graph(&graph_f)); - let gvw = g.window(w.start, w.end).valid(); - let gmw = gvw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gvw, &gmw); - }) - } - - #[test] - fn materialize_window_valid_events_prop_test() { - proptest!(|(graph_f in build_graph_strat(10, 10, 10, 10, true), w in any::>())| { - let g = Graph::from(build_graph(&graph_f)); - let gvw = g.window(w.start, w.end).valid(); - let gmw = gvw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gvw, &gmw); - }) - } - - #[test] - fn broken_earliest_time() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.delete_edge(1, 0, 1, None).unwrap(); - let gv = g.valid(); - let expected = PersistentGraph::new(); - expected.resolve_layer(None).unwrap(); - assert_graph_equal(&gv, &expected); - let gm = gv.materialize().unwrap(); - assert_graph_equal(&gv, &gm); - } - - #[test] - fn broken_earliest_time2() { - let g = PersistentGraph::new(); - - g.add_edge(10, 0, 0, NO_PROPS, None).unwrap(); - g.delete_edge(0, 0, 0, None).unwrap(); - - let w = 1..11; - - let gv = g.valid(); - assert_eq!(gv.node(0).unwrap().earliest_time().map(|t| t.t()), Some(0)); - - let gvw = gv.window(w.start, w.end); - assert_eq!( - gvw.node(0).unwrap().earliest_time().map(|t| t.t()), - Some(10) - ); - - assert_eq!(gvw.node(0).unwrap().history(), [10]); - - let gvwm = gvw.materialize().unwrap(); - assert_eq!( - gvwm.node(0).unwrap().earliest_time().map(|t| t.t()), - Some(10) - ); - } - - #[test] - fn broken_earliest_time3() { - let g = PersistentGraph::new(); - g.add_edge(1, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); - g.delete_edge(100, 0, 0, None).unwrap(); - let gvw = g.valid().window(2, 20); - assert_eq!( - gvw.node(0).unwrap().earliest_time().map(|t| t.t()), - Some(10) - ); - let gvwm = gvw.materialize().unwrap(); - println!("{:?}", gvwm); - assert_eq!( - gvwm.node(0).unwrap().earliest_time().map(|t| t.t()), - Some(10) - ); - } - - #[test] - fn missing_temporal_edge() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 0, NO_PROPS, None).unwrap(); - g.add_edge(7658643179498972033, 0, 0, NO_PROPS, None) - .unwrap(); - g.add_edge(781965068308597440, 0, 0, NO_PROPS, Some("b")) - .unwrap(); - let gv = g.valid(); - assert_graph_equal(&gv, &g); - let gvm = gv.materialize().unwrap(); - println!("{:?}", gvm); - assert_graph_equal(&gv, &gvm); - } - - #[test] - fn wrong_temporal_edge_count() { - let g = PersistentGraph::new(); - g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.add_edge(1, 0, 1, NO_PROPS, None).unwrap(); - g.add_edge(2, 1, 0, NO_PROPS, None).unwrap(); - g.add_edge(3, 1, 0, NO_PROPS, Some("b")).unwrap(); - let gw = g.valid().window(0, 9); - let gwm = gw.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gw, &gwm); // PersistentGraph ignores the earliest time's event id - } - - #[test] - fn mismatched_edge_properties() { - let g = PersistentGraph::new(); - g.add_edge(2, 1, 0, [("test", 1)], Some("b")).unwrap(); - g.add_edge(10, 1, 0, NO_PROPS, None).unwrap(); - g.add_edge(0, 0, 1, NO_PROPS, None) - .unwrap() - .add_metadata([("const_test", 2)], None) - .unwrap(); - - let gw = g.valid().window(-1, 1); - assert_eq!( - gw.edge(0, 1).unwrap().metadata().get("const_test").unwrap(), - Prop::map([("_default", 2)]) - ); - assert_eq!( - gw.edge(0, 1) - .unwrap() - .default_layer() - .metadata() - .get("const_test") - .unwrap(), - 2.into() - ); - assert_graph_equal(&gw, &gw.materialize().unwrap()); - } - - #[test] - fn node_earliest_time() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - g.add_edge(0, 0, 2, NO_PROPS, None).unwrap(); - g.add_edge(2, 0, 2, NO_PROPS, None).unwrap(); - g.delete_edge(-10, 0, 1, None).unwrap(); - - let gv = g.valid().window(-1, 10); - let gvm = gv.materialize().unwrap(); - assert_graph_equal(&gv, &gvm); - assert_eq!(gv.node(0).unwrap().earliest_time().map(|t| t.t()), Some(0)); - } - - #[test] - fn broken_degree() { - let g = PersistentGraph::new(); - g.add_edge(0, 5, 4, NO_PROPS, None).unwrap(); - g.add_edge(0, 4, 9, NO_PROPS, None).unwrap(); - g.add_edge(0, 4, 6, NO_PROPS, None).unwrap(); - g.add_edge(0, 4, 6, NO_PROPS, Some("b")).unwrap(); - g.add_edge(1, 4, 9, NO_PROPS, Some("a")).unwrap(); - g.add_edge(2, 5, 4, NO_PROPS, Some("a")).unwrap(); - g.delete_edge(10, 5, 4, None).unwrap(); - - let gv = g.valid().window(0, 20); - assert!(!gv.default_layer().has_edge(5, 4)); - assert_eq!(gv.edge(5, 4).unwrap().latest_time().map(|t| t.t()), Some(2)); - assert_eq!(gv.earliest_time().map(|t| t.t()), Some(0)); - assert_eq!(gv.latest_time().map(|t| t.t()), Some(2)); - assert_eq!(gv.node(6).unwrap().latest_time().map(|t| t.t()), Some(0)); - let expected = PersistentGraph::new(); - expected.add_edge((0, 1), 4, 9, NO_PROPS, None).unwrap(); - expected.add_edge((0, 2), 4, 6, NO_PROPS, None).unwrap(); - expected - .add_edge((0, 3), 4, 6, NO_PROPS, Some("b")) - .unwrap(); - expected - .add_edge((1, 4), 4, 9, NO_PROPS, Some("a")) - .unwrap(); - expected - .add_edge((2, 5), 5, 4, NO_PROPS, Some("a")) - .unwrap(); - - assert_persistent_materialize_graph_equal(&gv, &expected); // PersistentGraph ignores the earliest time's event id - - let n4 = gv.node(4).unwrap(); - assert_eq!(n4.out_degree(), 2); - assert_eq!(n4.in_degree(), 1); - - assert_persistent_materialize_graph_equal(&gv, &gv.materialize().unwrap()); - // PersistentGraph ignores the earliest time's event id - } - - #[test] - fn weird_empty_graph() { - let g = PersistentGraph::new(); - g.add_edge(10, 2, 1, NO_PROPS, Some("a")).unwrap(); - g.delete_edge(5, 2, 1, None).unwrap(); - g.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); - let gvw = g.valid().window(0, 5); - assert_eq!(gvw.count_nodes(), 0); - let expected = PersistentGraph::new(); - expected.resolve_layer(None).unwrap(); - expected.resolve_layer(Some("a")).unwrap(); - assert_graph_equal(&gvw, &expected); - let gvwm = gvw.materialize().unwrap(); - assert_graph_equal(&gvw, &gvwm); - } - - #[test] - fn mismatched_node_earliest_time_again() { - let g = PersistentGraph::new(); - g.add_node(-2925244660385668056, 1, NO_PROPS, None, None) - .unwrap(); - g.add_edge(1116793271088085151, 2, 1, NO_PROPS, Some("a")) - .unwrap(); - g.add_edge(0, 9, 1, NO_PROPS, None).unwrap(); - g.delete_edge(7891470373966857988, 9, 1, None).unwrap(); - g.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); - - let gv = g.window(-2925244660385668055, 7060945172792084486).valid(); - assert_persistent_materialize_graph_equal(&gv, &gv.materialize().unwrap()); - } - - #[test] - fn test_single_edge() { - let g = PersistentGraph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - let gwv = g.window(10, 11).valid(); - let gm = gwv.materialize().unwrap(); - assert_persistent_materialize_graph_equal(&gwv, &gm); - } -} diff --git a/raphtory/tests/views_test.rs b/raphtory/tests/views_test.rs deleted file mode 100644 index 3aed3a64e5..0000000000 --- a/raphtory/tests/views_test.rs +++ /dev/null @@ -1,4607 +0,0 @@ -#[cfg(all(test, feature = "test-utils"))] -mod test { - use itertools::Itertools; - use proptest::{prop_assert, prop_assert_eq, prop_assume, proptest}; - use rand::{prelude::*, rng}; - use raphtory::{ - algorithms::centrality::degree_centrality::degree_centrality, - db::graph::{graph::assert_graph_equal, views::window_graph::WindowedGraph}, - prelude::*, - test_storage, - test_utils::test_graph, - }; - use raphtory_api::core::{ - entities::GID, - storage::timeindex::AsTime, - utils::{logging::global_info_logger, time::IntoTime}, - }; - use rayon::prelude::*; - use std::ops::Range; - use tracing::{error, info}; - - #[test] - fn test_non_restricted_window() { - let g = Graph::new(); - g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - for n in g.window(0, 1).nodes() { - assert!(g.has_node(n)); - } - - assert_graph_equal(&g.window(0, 1), &g) - } - - #[test] - fn windowed_graph_nodes_degree() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let wg = graph.window(-1, 1); - - let actual = wg - .nodes() - .iter() - .map(|v| (v.id(), v.degree())) - .collect::>(); - - let expected = vec![(GID::U64(1), 2), (GID::U64(2), 1)]; - - assert_eq!(actual, expected); - }); - } - - #[test] - fn windowed_graph_edge() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in vs { - graph.add_edge(t, src, dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let wg = graph.window(i64::MIN, i64::MAX); - assert_eq!(wg.edge(1, 3).unwrap().src().id(), GID::U64(1)); - assert_eq!(wg.edge(1, 3).unwrap().dst().id(), GID::U64(3)); - }); - } - - #[test] - fn windowed_graph_node_edges() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let wg = graph.window(-1, 1); - - assert_eq!(wg.node(1).unwrap().id(), GID::U64(1)); - }); - } - - #[test] - fn graph_has_node_check_fail() { - let vs: Vec<(i64, u64)> = vec![ - (1, 0), - (-100, 262), - // (327226439, 108748364996394682), - (1, 9135428456135679950), - // (0, 1), - // (2, 2), - ]; - let graph = Graph::new(); - - for (t, v) in &vs { - graph.add_node(*t, *v, NO_PROPS, None, None).unwrap(); - } - - // FIXME: Issue #46: arrow_test(&graph, test) - test_graph(&graph, |graph| { - let wg = graph.window(1, 2); - assert!(!wg.has_node(262)) - }); - } - - #[test] - fn windowed_graph_has_node() { - proptest!(|(mut vs: Vec<(i64, u64)>)| { - global_info_logger(); - prop_assume!(!vs.is_empty()); - - vs.sort_by_key(|v| v.1); // Sorted by node - vs.dedup_by_key(|v| v.1); // Have each node only once to avoid headaches - vs.sort_by_key(|v| v.0); // Sorted by time - - let rand_start_index = rng().random_range(0..vs.len()); - let rand_end_index = rng().random_range(rand_start_index..vs.len()); - - let g = Graph::new(); - - for (t, v) in &vs { - g.add_node(*t, *v, NO_PROPS, None, None) - .map_err(|err| error!("{:?}", err)) - .ok(); - } - - let start = vs.get(rand_start_index).expect("start index in range").0; - let end = vs.get(rand_end_index).expect("end index in range").0; - - let wg = g.window(start, end); - - let rand_test_index: usize = rng().random_range(0..vs.len()); - - let (i, v) = vs.get(rand_test_index).expect("test index in range"); - if (start..end).contains(i) { - prop_assert!(wg.has_node(*v), "Node {:?} was not in window {:?}", (i, v), start..end); - } else { - prop_assert!(!wg.has_node(*v), "Node {:?} was in window {:?}", (i, v), start..end); - } - }); - } - - #[test] - fn windowed_graph_has_edge() { - proptest!(|(mut edges: Vec<(i64, (u64, u64))>)| { - prop_assume!(!edges.is_empty()); - - edges.sort_by_key(|e| e.1); // Sorted by edge - edges.dedup_by_key(|e| e.1); // Have each edge only once to avoid headaches - edges.sort_by_key(|e| e.0); // Sorted by time - - let rand_start_index = rng().random_range(0..edges.len()); - let rand_end_index = rng().random_range(rand_start_index..edges.len()); - - let g = Graph::new(); - - for (t, e) in &edges { - g.add_edge(*t, e.0, e.1, NO_PROPS, None).unwrap(); - } - - let start = edges.get(rand_start_index).expect("start index in range").0; - let end = edges.get(rand_end_index).expect("end index in range").0; - - let wg = g.window(start, end); - - let rand_test_index: usize = rng().random_range(0..edges.len()); - - let (i, e) = edges.get(rand_test_index).expect("test index in range"); - if (start..end).contains(i) { - prop_assert!(wg.has_edge(e.0, e.1), "Edge {:?} was not in window {:?}", (i, e), start..end); - } else { - prop_assert!(!wg.has_edge(e.0, e.1), "Edge {:?} was in window {:?}", (i, e), start..end); - } - }); - } - - #[test] - fn windowed_graph_edge_count() { - proptest!(|(mut edges: Vec<(i64, (u64, u64))>, window: Range)| { - global_info_logger(); - prop_assume!(window.end >= window.start); - - edges.sort_by_key(|e| e.1); // Sorted by edge - edges.dedup_by_key(|e| e.1); // Have each edge only once to avoid headaches - - let true_edge_count = edges.iter().filter(|e| window.contains(&e.0)).count(); - - let g = Graph::new(); - - for (t, e) in &edges { - g.add_edge(*t, e.0, e.1, [("test".to_owned(), Prop::Bool(true))], None) - .unwrap(); - } - - let wg = g.window(window.start, window.end); - if wg.count_edges() != true_edge_count { - info!( - "failed, g.num_edges() = {}, true count = {}", - wg.count_edges(), - true_edge_count - ); - info!("g.edges() = {:?}", wg.edges().iter().collect_vec()); - } - prop_assert_eq!(wg.count_edges(), true_edge_count); - }); - } - - #[test] - fn trivial_window_has_all_edges() { - proptest!(|(edges: Vec<(i64, u64, u64)>)| { - let g = Graph::new(); - edges - .into_par_iter() - .filter(|e| e.0 < i64::MAX) - .for_each(|(t, src, dst)| { - g.add_edge(t, src, dst, [("test".to_owned(), Prop::Bool(true))], None) - .unwrap(); - }); - let w = g.window(i64::MIN, i64::MAX); - prop_assert!(g.edges() - .iter() - .all(|e| w.has_edge(e.src().id(), e.dst().id()))); - }); - } - - #[test] - fn large_node_in_window() { - proptest!(|(dsts: Vec)| { - let dsts: Vec = dsts.into_iter().unique().collect(); - let n = dsts.len(); - let g = Graph::new(); - - for dst in dsts { - let t = 1; - g.add_edge(t, 0, dst, NO_PROPS, None).unwrap(); - } - let w = g.window(i64::MIN, i64::MAX); - prop_assert_eq!(w.count_edges(), n); - }); - } - - #[test] - fn windowed_graph_node_ids() { - let vs = vec![(1, 1, 2), (3, 3, 4), (5, 5, 6), (7, 7, 1)]; - - let args = [(i64::MIN, 8), (i64::MIN, 2), (i64::MIN, 4), (3, 6)]; - - let expected = vec![ - vec![1, 2, 3, 4, 5, 6, 7], - vec![1, 2], - vec![1, 2, 3, 4], - vec![3, 4, 5, 6], - ]; - - let graph = Graph::new(); - - for (t, src, dst) in &vs { - graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - let res: Vec<_> = (0..=3) - .map(|i| { - let wg = graph.window(args[i].0, args[i].1); - let mut e = wg - .nodes() - .id() - .iter_values() - .filter_map(|id| id.to_u64()) - .collect::>(); - e.sort(); - e - }) - .collect_vec(); - - assert_eq!(res, expected); - }); - - let graph = Graph::new(); - for (src, dst, t) in &vs { - graph.add_edge(*src, *dst, *t, NO_PROPS, None).unwrap(); - } - test_storage!(&graph, |graph| { - let res: Vec<_> = (0..=3) - .map(|i| { - let wg = graph.window(args[i].0, args[i].1); - let mut e = wg - .nodes() - .id() - .iter_values() - .filter_map(|id| id.to_u64()) - .collect::>(); - e.sort(); - e - }) - .collect_vec(); - assert_eq!(res, expected); - }); - } - - #[test] - fn windowed_graph_nodes() { - let vs = vec![ - (1, 1, 2), - (2, 1, 3), - (-1, 2, 1), - (0, 1, 1), - (7, 3, 2), - (1, 1, 1), - ]; - - let graph = Graph::new(); - - graph - .add_node( - 0, - 1, - [("type", "wallet".into_prop()), ("cost", 99.5.into_prop())], - None, - None, - ) - .unwrap(); - - graph - .add_node( - -1, - 2, - [("type", "wallet".into_prop()), ("cost", 10.0.into_prop())], - None, - None, - ) - .unwrap(); - - graph - .add_node( - 6, - 3, - [("type", "wallet".into_prop()), ("cost", 76.2.into_prop())], - None, - None, - ) - .unwrap(); - - for (t, src, dst) in &vs { - graph - .add_edge(*t, *src, *dst, [("eprop", "commons")], None) - .unwrap(); - } - test_storage!(&graph, |graph| { - let wg = graph.window(-2, 0); - - let actual = wg - .nodes() - .id() - .iter_values() - .filter_map(|id| id.to_u64()) - .collect::>(); - - let expected = vec![1, 2]; - - assert_eq!(actual, expected); - }); - } - - #[test] - fn test_reference() { - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - - test_storage!(&graph, |graph| { - let mut w = WindowedGraph::new(&graph, Some(0.into_time()), Some(1.into_time())); - assert_eq!(w, graph); - w = WindowedGraph::new(&graph, Some(1.into_time()), Some(2.into_time())); - assert_eq!(w, Graph::new()); - }); - } - - #[test] - fn test_algorithm_on_windowed_graph() { - global_info_logger(); - let graph = Graph::new(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - test_storage!(&graph, |graph| { - let w = graph.window(0, 1); - let _ = degree_centrality(&w); - }); - } - - #[test] - fn test_view_resetting() { - let graph = Graph::new(); - for t in 0..10 { - let t1 = t * 3; - let t2 = t * 3 + 1; - let t3 = t * 3 + 2; - graph.add_edge(t1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(t2, 2, 3, NO_PROPS, None).unwrap(); - graph.add_edge(t3, 3, 1, NO_PROPS, None).unwrap(); - } - - test_storage!(&graph, |graph| { - assert_graph_equal(&graph.before(9).after(2), &graph.window(3, 9)); - let res = graph - .window(3, 9) - .nodes() - .before(6) - .edges() - .window(1, 9) - .earliest_time() - .map(|it| it.map(|t_opt| t_opt.map(|t| t.t())).collect_vec()) - .collect_vec(); - assert_eq!( - res, - [[Some(3), Some(5)], [Some(3), Some(4)], [Some(5), Some(4)]] - ); - }); - } - - #[test] - fn test_entity_history() { - let graph = Graph::new(); - graph.add_node(0, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(1, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(2, 0, NO_PROPS, None, None).unwrap(); - graph.add_node(3, 0, NO_PROPS, None, None).unwrap(); - graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(1, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); - graph.add_edge(4, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(5, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(6, 1, 3, NO_PROPS, None).unwrap(); - graph.add_edge(7, 1, 3, NO_PROPS, None).unwrap(); - - // FIXME: Issue #46 - test_graph(&graph, |graph| { - let e = graph.edge(1, 2).unwrap(); - let v = graph.node(0).unwrap(); - let full_history_1 = vec![0i64, 1, 2, 3]; - - let full_history_2 = vec![4i64, 5, 6, 7]; - - let windowed_history = vec![0i64, 1]; - - assert_eq!(v.history(), full_history_1); - - assert_eq!(v.window(0, 2).history(), windowed_history); - assert_eq!(e.history(), full_history_1); - assert_eq!(e.window(0, 2).history(), windowed_history); - - assert_eq!( - graph.edges().history().collect_vec(), - [full_history_1.clone(), full_history_2.clone()] - ); - assert_eq!( - graph - .nodes() - .in_edges() - .history() - .map(|it| it.collect_vec()) - .collect_vec(), - [vec![], vec![], vec![full_history_1], vec![full_history_2],] - ); - - assert_eq!( - graph - .nodes() - .earliest_time() - .iter_values() - .flatten() - .collect_vec(), - [0, 0, 0, 4,] - ); - - assert_eq!( - graph - .nodes() - .latest_time() - .iter_values() - .flatten() - .collect_vec(), - [3, 7, 3, 7] - ); - - assert_eq!( - graph - .nodes() - .neighbours() - .latest_time() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, it)| it.flatten().collect_vec()) - .collect_vec(), - [vec![], vec![3, 7], vec![7], vec![7],] - ); - - assert_eq!( - graph - .nodes() - .neighbours() - .earliest_time() - .sorted_by_key(|(n, _)| n.id()) - .map(|(_, it)| it.flatten().collect_vec()) - .collect_vec(), - [vec![], vec![0, 4], vec![0], vec![0],] - ); - }); - } - - mod test_filters_window_graph { - - mod test_nodes_filters_window_graph { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::{ - assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, - TestGraphVariants, TestVariants, - }, - views::filter::model::ComposableFilter, - }, - }, - prelude::{AdditionOps, PropertyAdditionOps}, - }; - use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; - use raphtory_storage::mutation::{ - addition_ops::InternalAdditionOps, - property_addition_ops::InternalPropertyAdditionOps, - }; - - use raphtory::{ - db::{ - api::view::filter_ops::Filter, - graph::{ - assertions::WindowGraphTransformer, - views::filter::model::{ - node_filter::{ops::NodeFilterOps, NodeFilter}, - property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, - }, - }, - }, - errors::GraphError, - prelude::{Graph, GraphViewOps, TimeOps}, - }; - - fn init_graph( - graph: G, - ) -> G { - let nodes = vec![ - ( - 6, - "N1", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ( - 7, - "N1", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(5i64)), - ("k3", Prop::Bool(false)), - ], - Some("air_nomad"), - ), - ( - 6, - "N2", - vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(6.0f64))], - Some("water_tribe"), - ), - ( - 7, - "N2", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("water_tribe"), - ), - (8, "N3", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (9, "N4", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - ( - 5, - "N5", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ( - 6, - "N5", - vec![ - ("p1", Prop::U64(2u64)), - ("k2", Prop::Str(ArcStr::from("Pometry"))), - ("k4", Prop::F64(1.0f64)), - ], - Some("air_nomad"), - ), - (5, "N6", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - ( - 6, - "N6", - vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(1.0f64))], - Some("fire_nation"), - ), - ( - 3, - "N7", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("air_nomad"), - ), - (5, "N7", vec![("p1", Prop::U64(1u64))], Some("air_nomad")), - (3, "N8", vec![("p1", Prop::U64(1u64))], Some("fire_nation")), - ( - 4, - "N8", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("fire_nation"), - ), - (2, "N9", vec![("p1", Prop::U64(2u64))], None), - (2, "N10", vec![("q1", Prop::U64(0u64))], None), - (2, "N10", vec![("p1", Prop::U64(3u64))], None), - (2, "N11", vec![("p1", Prop::U64(3u64))], None), - (2, "N11", vec![("q1", Prop::U64(0u64))], None), - (2, "N12", vec![("q1", Prop::U64(0u64))], None), - ( - 3, - "N12", - vec![ - ("p1", Prop::U64(3u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - None, - ), - (2, "N13", vec![("q1", Prop::U64(0u64))], None), - (3, "N13", vec![("p1", Prop::U64(3u64))], None), - (2, "N14", vec![("q1", Prop::U64(0u64))], None), - (2, "N15", vec![], None), - ]; - - // Add nodes to the graph - for (id, name, props, layer) in &nodes { - graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); - } - - // Metadata property assignments - let metadata = vec![ - ( - "N1", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(3i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - ), - ("N4", vec![("p1", Prop::U64(2u64))]), - ("N9", vec![("p1", Prop::U64(1u64))]), - ("N10", vec![("p1", Prop::U64(1u64))]), - ("N11", vec![("p1", Prop::U64(1u64))]), - ("N12", vec![("p1", Prop::U64(1u64))]), - ( - "N13", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - ), - ("N14", vec![("p1", Prop::U64(1u64))]), - ("N15", vec![("p1", Prop::U64(1u64))]), - ]; - - // Apply metadata - for (node, props) in metadata { - graph.node(node).unwrap().add_metadata(props).unwrap(); - } - - graph - } - - fn init_graph2< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - let nodes = vec![( - 2, - "N14", - vec![ - ("q1", Prop::U64(0u64)), - ( - "x", - Prop::list(vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)]), - ), - ], - None, - )]; - - // Add nodes to the graph - for (id, name, props, layer) in &nodes { - graph - .add_node(*id, name, props.clone(), *layer, None) - .unwrap(); - } - - graph - } - - #[test] - fn test_nodes_filters_for_node_name_eq() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::name().eq("N2"); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_pg_for_node_name_eq() { - let filter = NodeFilter::name().eq("N2"); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_node_name_ne() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::name().ne("N2"); - let expected_results = vec!["N1", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_node_name_ne() { - let filter = NodeFilter::name().ne("N2"); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N3", "N5", "N6", "N7", "N8", - "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_node_name_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::name().is_in(vec!["N2"]); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = NodeFilter::name().is_in(vec!["N2", "N5"]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_pg_for_node_name_in() { - let filter = NodeFilter::name().is_in(vec!["N2"]); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter::name().is_in(vec!["N2", "N5"]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_node_name_not_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::name().is_not_in(vec!["N5"]); - let expected_results = vec!["N1", "N2", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_node_name_not_in() { - let filter = NodeFilter::name().is_not_in(vec!["N5"]); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N6", "N7", "N8", - "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_node_type_eq() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::node_type().eq("fire_nation"); - let expected_results = vec!["N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_node_type_eq() { - let filter = NodeFilter::node_type().eq("fire_nation"); - let expected_results = vec!["N6", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_node_type_ne() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::node_type().ne("fire_nation"); - let expected_results = vec!["N1", "N2", "N3", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_node_type_ne() { - let filter = NodeFilter::node_type().ne("fire_nation"); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N5", "N7", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_node_type_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); - let expected_results = vec!["N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomad"]); - let expected_results = vec!["N1", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_node_type_in() { - let filter = NodeFilter::node_type().is_in(vec!["fire_nation"]); - let expected_results = vec!["N6", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter::node_type().is_in(vec!["fire_nation", "air_nomad"]); - let expected_results = vec!["N1", "N3", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_node_type_not_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); - let expected_results = vec!["N1", "N2", "N3", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_node_type_not_in() { - let filter = NodeFilter::node_type().is_not_in(vec!["fire_nation"]); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N14", "N15", "N2", "N3", "N5", "N7", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_eq() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").eq(2i64); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k2").eq("Paper_Airplane"); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = NodeFilter.property("k3").eq(true); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = NodeFilter.property("k4").eq(6.0f64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = NodeFilter.property("x").eq(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = vec!["N14"]; - // TODO: List(U64) not supported as disk_graph property - // assert_filter_nodes_results_w!( - // init_graph2, - // filter, - // 1..9, - // expected_results, - // variants = [graph] - // ); - assert_filter_nodes_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - // TODO: Search APIs don't support list yet - // assert_search_nodes_results( - // init_graph, - // WindowGraphTransformer(6..9), - // filter, - // &expected_results, - // TestVariants::EventOnly, - // ); - } - - #[test] - fn test_nodes_filters_pg_for_property_eq() { - let filter = NodeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").eq(2i64); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k2").eq("Paper_Airplane"); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - // TODO: Const properties not supported for disk_graph. - let filter = NodeFilter.property("k3").eq(true); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").eq(6.0f64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("x").eq(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = vec!["N14"]; - // TODO: List(U64) not supported as disk_graph property - // assert_filter_nodes_results_pg_w!( - // init_graph2, - // filter, - // 1..9, - // expected_results, - // variants = [persistent_graph] - // ); - assert_filter_nodes_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - // TODO: Search APIs don't support list yet - // assert_search_nodes_results( - // init_graph, - // WindowGraphTransformer(6..9), - // filter, - // &expected_results, - // vec![TestGraphVariants::PersistentGraph], - // ); - } - - #[test] - fn test_nodes_filters_for_property_ne() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").ne(1u64); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").ne(2i64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k2").ne("Paper_Airplane"); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k3").ne(true); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").ne(6.0f64); - let expected_results = vec!["N2", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_ne() { - let filter = NodeFilter.property("p1").ne(1u64); - let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").ne(2i64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k2").ne("Paper_Airplane"); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k3").ne(true); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").ne(6.0f64); - let expected_results = vec!["N12", "N2", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_lt() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").lt(3u64); - let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").lt(3i64); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").lt(10.0f64); - let expected_results = vec!["N1", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_lt() { - let filter = NodeFilter.property("p1").lt(3u64); - let expected_results = vec!["N1", "N2", "N3", "N5", "N6", "N7", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").lt(3i64); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").lt(10.0f64); - let expected_results = vec!["N1", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_le() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N1", "N3", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").le(2i64); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").le(6.0f64); - let expected_results = vec!["N1", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_le() { - let filter = NodeFilter.property("p1").le(1u64); - let expected_results = vec!["N1", "N3", "N6", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").le(2i64); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").le(6.0f64); - let expected_results = vec!["N1", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_gt() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").gt(2i64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").gt(6.0f64); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("x").gt(Prop::List( - vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)].into(), - )); - let graph = init_graph(Graph::new()); - assert!(matches!( - graph.window(1, 9).filter(filter.clone()).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "x" - )); - assert!(matches!( - graph.persistent_graph().window(1, 9).filter(filter).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "x" - )); - } - - #[test] - fn test_nodes_filters_pg_for_property_gt() { - let filter = NodeFilter.property("p1").gt(1u64); - let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").gt(2i64); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").gt(6.0f64); - let expected_results = vec!["N12", "N2", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_ge() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").ge(1u64); - let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").ge(2i64); - let expected_results = vec!["N1", "N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").ge(6.0f64); - let expected_results = vec!["N1", "N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_ge() { - let filter = NodeFilter.property("p1").ge(1u64); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N5", "N6", "N7", "N8", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").ge(2i64); - let expected_results = vec!["N1", "N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").ge(6.0f64); - let expected_results = vec!["N1", "N12", "N2", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").is_in(vec![2u64.into()]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").is_in(vec![2i64.into()]); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter - .property("k2") - .is_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k3").is_in(vec![true.into()]); - let expected_results = vec!["N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").is_in(vec![6.0f64.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_in() { - let filter = NodeFilter.property("p1").is_in(vec![2u64.into()]); - let expected_results = vec!["N2", "N5", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").is_in(vec![2i64.into()]); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter - .property("k2") - .is_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - // TODO: Const properties not supported for disk_graph. - let filter = NodeFilter.property("k3").is_in(vec![true.into()]); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").is_in(vec![6.0f64.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_not_in() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").is_not_in(vec![1u64.into()]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k1").is_not_in(vec![2i64.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter - .property("k2") - .is_not_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N2", "N5"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k3").is_not_in(vec![true.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k4").is_not_in(vec![6.0f64.into()]); - let expected_results = vec!["N2", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_not_in() { - let filter = NodeFilter.property("p1").is_not_in(vec![1u64.into()]); - let expected_results = vec!["N10", "N11", "N12", "N13", "N2", "N5", "N8", "N9"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k1").is_not_in(vec![2i64.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter - .property("k2") - .is_not_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N12", "N2", "N5", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k3").is_not_in(vec![true.into()]); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k4").is_not_in(vec![6.0f64.into()]); - let expected_results = vec!["N12", "N2", "N5", "N6", "N7", "N8"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_property_is_some() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("p1").is_some(); - let expected_results = vec!["N1", "N2", "N3", "N5", "N6"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(1..2), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(1..2), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(10..12), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(10..12), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_for_property_is_some() { - let filter = NodeFilter.property("p1").is_some(); - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N5", "N6", "N7", "N8", "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(1..2), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(1..2), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let expected_results = vec![ - "N1", "N10", "N11", "N12", "N13", "N2", "N3", "N4", "N5", "N6", "N7", "N8", - "N9", - ]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(10..12), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(10..12), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_for_props_added_at_different_times() { - let filter = NodeFilter - .property("q1") - .eq(0u64) - .and(NodeFilter.property("p1").eq(3u64)); - let expected_results = vec!["N10", "N11", "N12", "N13"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(1..4), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(1..4), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_nodes_filters_pg_for_props_added_at_different_times() { - let filter = NodeFilter - .property("q1") - .eq(0u64) - .and(NodeFilter.property("p1").eq(3u64)); - let expected_results = vec!["N10", "N11", "N12", "N13"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_fuzzy_search() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter - .property("k2") - .fuzzy_search("Paper_Airpla", 2, false); - let expected_results = vec!["N1"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_fuzzy_search() { - let filter = NodeFilter - .property("k2") - .fuzzy_search("Paper_Air", 5, false); - let expected_results = vec!["N1", "N2", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_nodes_filters_fuzzy_search_prefix_match() { - // TODO: Enable event_disk_graph once bug fixed: https://github.com/Pometry/Raphtory/issues/2098 - let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec!["N1", "N2"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - - let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, false); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::Graph], - ); - } - - #[test] - fn test_nodes_filters_pg_fuzzy_search_prefix_match() { - let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec!["N1", "N2", "N7"]; - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = NodeFilter.property("k2").fuzzy_search("Pa", 2, false); - let expected_results = Vec::<&str>::new(); - assert_filter_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_nodes_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - - mod test_edges_filters_window_graph { - use raphtory::{ - db::{ - api::view::{filter_ops::Filter, StaticGraphViewOps}, - graph::{ - assertions::{ - assert_filter_edges_results, assert_search_edges_results, - TestGraphVariants, TestVariants, WindowGraphTransformer, - }, - views::filter::model::{ - edge_filter::EdgeFilter, node_filter::ops::NodeFilterOps, - property_filter::ops::PropertyFilterOps, ComposableFilter, - PropertyFilterFactory, - }, - }, - }, - errors::GraphError, - prelude::{ - AdditionOps, Graph, GraphViewOps, PropertyAdditionOps, TimeOps, NO_PROPS, - }, - }; - use raphtory_api::core::{entities::properties::prop::Prop, storage::arc_str::ArcStr}; - - fn init_graph( - graph: G, - ) -> G { - let edges = vec![ - ( - 6, - "N1", - "N2", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ( - 7, - "N1", - "N2", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(5i64)), - ("k3", Prop::Bool(false)), - ], - Some("air_nomad"), - ), - ( - 6, - "N2", - "N3", - vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(6.0f64))], - Some("water_tribe"), - ), - ( - 7, - "N2", - "N3", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("water_tribe"), - ), - ( - 8, - "N3", - "N4", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - ( - 9, - "N4", - "N5", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - ( - 5, - "N5", - "N6", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ( - 6, - "N5", - "N6", - vec![ - ("p1", Prop::U64(2u64)), - ("k2", Prop::Str(ArcStr::from("Pometry"))), - ("k4", Prop::F64(1.0f64)), - ], - Some("air_nomad"), - ), - ( - 5, - "N6", - "N7", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - ( - 6, - "N6", - "N7", - vec![("p1", Prop::U64(1u64)), ("k4", Prop::F64(1.0f64))], - Some("fire_nation"), - ), - ( - 3, - "N7", - "N8", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Ship"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("air_nomad"), - ), - ( - 5, - "N7", - "N8", - vec![("p1", Prop::U64(1u64))], - Some("air_nomad"), - ), - ( - 3, - "N8", - "N9", - vec![("p1", Prop::U64(1u64))], - Some("fire_nation"), - ), - ( - 4, - "N8", - "N9", - vec![ - ("p1", Prop::U64(2u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - Some("fire_nation"), - ), - (2, "N9", "N10", vec![("p1", Prop::U64(2u64))], None), - (2, "N10", "N11", vec![("q1", Prop::U64(0u64))], None), - (2, "N10", "N11", vec![("p1", Prop::U64(3u64))], None), - (2, "N11", "N12", vec![("p1", Prop::U64(3u64))], None), - (2, "N11", "N12", vec![("q1", Prop::U64(0u64))], None), - (2, "N12", "N13", vec![("q1", Prop::U64(0u64))], None), - ( - 3, - "N12", - "N13", - vec![ - ("p1", Prop::U64(3u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - None, - ), - (2, "N13", "N14", vec![("q1", Prop::U64(0u64))], None), - (3, "N13", "N14", vec![("p1", Prop::U64(3u64))], None), - (2, "N14", "N15", vec![("q1", Prop::U64(0u64))], None), - (2, "N15", "N1", vec![], None), - ]; - - for (id, src, dst, props, layer) in &edges { - graph - .add_edge(*id, src, dst, props.clone(), *layer) - .unwrap(); - } - - // Metadata property assignments - let metadata = vec![ - ( - "N1", - "N2", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(3i64)), - ("k2", Prop::Str(ArcStr::from("Paper_Airplane"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(6.0f64)), - ], - Some("air_nomad"), - ), - ("N4", "N5", vec![("p1", Prop::U64(2u64))], Some("air_nomad")), - ("N9", "N10", vec![("p1", Prop::U64(1u64))], None), - ("N10", "N11", vec![("p1", Prop::U64(1u64))], None), - ("N11", "N12", vec![("p1", Prop::U64(1u64))], None), - ("N12", "N13", vec![("p1", Prop::U64(1u64))], None), - ( - "N13", - "N14", - vec![ - ("p1", Prop::U64(1u64)), - ("k1", Prop::I64(2i64)), - ("k2", Prop::Str(ArcStr::from("Sand_Clown"))), - ("k3", Prop::Bool(true)), - ("k4", Prop::F64(10.0f64)), - ], - None, - ), - ("N14", "N15", vec![("p1", Prop::U64(1u64))], None), - ("N15", "N1", vec![("p1", Prop::U64(1u64))], None), - ]; - - for (src, dst, props, layer) in metadata { - graph - .edge(src, dst) - .unwrap() - .add_metadata(props, layer) - .unwrap(); - } - - graph.add_node(1, "N1", NO_PROPS, None, None).unwrap(); - graph.add_node(2, "N2", NO_PROPS, None, None).unwrap(); - graph.add_node(3, "N3", NO_PROPS, None, None).unwrap(); - - graph - } - - fn init_graph2( - graph: G, - ) -> G { - let edges = vec![( - 2, - "N14", - "N15", - vec![ - ("q1", Prop::U64(0u64)), - ( - "x", - Prop::list(vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)]), - ), - ], - None, - )]; - - for (id, src, dst, props, layer) in &edges { - graph - .add_edge(*id, src, dst, props.clone(), *layer) - .unwrap(); - } - - graph - } - - #[test] - fn test_edges_filters_for_src_eq() { - let filter = EdgeFilter::src().name().eq("N2"); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_src_eq() { - let filter = EdgeFilter::src().name().eq("N2"); - let expected_results = vec!["N2->N3"]; - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_src_ne() { - let filter = EdgeFilter::src().name().ne("N2"); - let expected_results = vec!["N1->N2", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_src_ne() { - let filter = EdgeFilter::src().name().ne("N2"); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", - "N15->N1", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", - ]; - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - } - - #[test] - fn test_edges_filters_for_dst_in() { - let filter = EdgeFilter::dst().name().is_in(vec!["N2"]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter::dst().name().is_in(vec!["N2", "N5"]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_dst_in() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter::dst().name().is_in(vec!["N2"]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter::dst().name().is_in(vec!["N2", "N5"]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_dst_not_in() { - let filter = EdgeFilter::dst().name().is_not_in(vec!["N5"]); - let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_dst_not_in() { - let filter = EdgeFilter::dst().name().is_not_in(vec!["N5"]); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N14->N15", - "N15->N1", "N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", - "N9->N10", - ]; - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - vec![], - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_property_eq() { - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").eq(2i64); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k2").eq("Paper_Airplane"); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k3").eq(true); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").eq(6.0f64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("x").eq(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = vec!["N14->N15"]; - // TODO: List(U64) not supported as disk_graph property - // assert_filter_edges_results_w!( - // init_graph2, - // filter, - // 1..9, - // expected_results, - // variants = [graph] - // ); - assert_filter_edges_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::Graph], - ); - // TODO: Search APIs don't support list yet - // assert_search_edges_results( - // init_graph2, - // WindowGraphTransformer(6..9), - // filter, - // &expected_results, - // TestVariants::PersistentOnly, - // ); - } - - #[test] - fn test_edges_filters_pg_for_property_eq() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").eq(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").eq(2i64); - - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k2").eq("Paper_Airplane"); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k3").eq(true); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").eq(6.0f64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("x").eq(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = vec!["N14->N15"]; - // TODO: List(U64) not supported as disk_graph property - // assert_filter_edges_results_pg_w!( - // init_graph2, - // filter, - // 1..9, - // expected_results, - // variants = [] - // ); - assert_filter_edges_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - // TODO: Search APIs don't support list yet - // assert_search_edges_results( - // init_graph2, - // WindowGraphTransformer(1..9), - // filter.clone(), - // &expected_results, - // vec![TestGraphVariants::PersistentGraph], - // ); - } - - #[test] - fn test_edges_filters_for_property_ne() { - let filter = EdgeFilter.property("p1").ne(1u64); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").ne(2i64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k2").ne("Paper_Airplane"); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k3").ne(true); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").ne(6.0f64); - let expected_results = vec!["N2->N3", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_property_ne() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").ne(1u64); - let expected_results = vec![ - "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", - "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").ne(2i64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k2").ne("Paper_Airplane"); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k3").ne(true); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").ne(6.0f64); - let expected_results = - vec!["N12->N13", "N2->N3", "N5->N6", "N6->N7", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("x").ne(Prop::list(vec![ - Prop::U64(1), - Prop::U64(6), - Prop::U64(9), - ])); - let expected_results = Vec::<&str>::new(); - assert_filter_edges_results( - init_graph2, - WindowGraphTransformer(1..9), - filter.clone(), - &expected_results, - vec![TestGraphVariants::PersistentGraph], - ); - // TODO: Search APIs don't support list yet - // assert_search_edges_results( - // init_graph2, - // WindowGraphTransformer(1..9), - // filter.clone(), - // &expected_results, - // TestVariants::PersistentOnly, - // ); - } - - #[test] - fn test_edges_filters_for_property_lt() { - let filter = EdgeFilter.property("p1").lt(3u64); - let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").lt(3i64); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").lt(10.0f64); - let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_property_lt() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").lt(3u64); - let expected_results = vec![ - "N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").lt(3i64); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").lt(10.0f64); - let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_property_le() { - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").le(2i64); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").le(6.0f64); - let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_property_le() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").le(1u64); - let expected_results = vec!["N1->N2", "N3->N4", "N6->N7", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").le(2i64); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").le(6.0f64); - let expected_results = vec!["N1->N2", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_property_gt() { - let filter = EdgeFilter.property("p1").gt(1u64); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").gt(2i64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").gt(6.0f64); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("x").gt(Prop::List( - vec![Prop::U64(1), Prop::U64(6), Prop::U64(9)].into(), - )); - let graph = init_graph(Graph::new()); - assert!(matches!( - graph.window(1, 9).filter(filter.clone()).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "x" - )); - assert!(matches!( - graph.persistent_graph().window(1, 9).filter(filter).unwrap_err(), - GraphError::PropertyMissingError(ref name) if name == "x" - )); - } - - #[test] - fn test_edges_filters_pg_for_property_gt() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").gt(1u64); - let expected_results = vec![ - "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", - "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").gt(2i64); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").gt(6.0f64); - let expected_results = vec!["N12->N13", "N2->N3", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_property_ge() { - let filter = EdgeFilter.property("p1").ge(1u64); - let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").ge(2i64); - let expected_results = vec!["N1->N2", "N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").ge(6.0f64); - let expected_results = vec!["N1->N2", "N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_property_ge() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").ge(1u64); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N3->N4", - "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").ge(2i64); - let expected_results = - vec!["N1->N2", "N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").ge(6.0f64); - let expected_results = vec!["N1->N2", "N12->N13", "N2->N3", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_property_in() { - let filter = EdgeFilter.property("p1").is_in(vec![2u64.into()]); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").is_in(vec![2i64.into()]); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter - .property("k2") - .is_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k3").is_in(vec![true.into()]); - let expected_results = vec!["N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").is_in(vec![6.0f64.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_property_in() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").is_in(vec![2u64.into()]); - let expected_results = vec!["N2->N3", "N5->N6", "N8->N9", "N9->N10"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").is_in(vec![2i64.into()]); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter - .property("k2") - .is_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k3").is_in(vec![true.into()]); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").is_in(vec![6.0f64.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_property_not_in() { - let filter = EdgeFilter.property("p1").is_not_in(vec![1u64.into()]); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k1").is_not_in(vec![2i64.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter - .property("k2") - .is_not_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N2->N3", "N5->N6"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k3").is_not_in(vec![true.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k4").is_not_in(vec![6.0f64.into()]); - let expected_results = vec!["N2->N3", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_property_not_in() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").is_not_in(vec![1u64.into()]); - let expected_results = vec![ - "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N8->N9", - "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k1").is_not_in(vec![2i64.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter - .property("k2") - .is_not_in(vec!["Paper_Airplane".into()]); - let expected_results = vec!["N12->N13", "N2->N3", "N5->N6", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k3").is_not_in(vec![true.into()]); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k4").is_not_in(vec![6.0f64.into()]); - let expected_results = - vec!["N12->N13", "N2->N3", "N5->N6", "N6->N7", "N7->N8", "N8->N9"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_property_is_some() { - let filter = EdgeFilter.property("p1").is_some(); - let expected_results = vec!["N1->N2", "N2->N3", "N3->N4", "N5->N6", "N6->N7"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - fn test_edges_filters_pg_for_property_is_some() { - // TODO: Const properties not supported for disk_graph. - let filter = EdgeFilter.property("p1").is_some(); - let expected_results = vec![ - "N1->N2", "N10->N11", "N11->N12", "N12->N13", "N13->N14", "N2->N3", "N3->N4", - "N5->N6", "N6->N7", "N7->N8", "N8->N9", "N9->N10", - ]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_for_src_dst() { - let filter = EdgeFilter::src() - .name() - .eq("N1") - .and(EdgeFilter::dst().name().eq("N2")); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::All, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::All, - ); - } - - #[test] - fn test_edges_filters_fuzzy_search() { - let filter = EdgeFilter - .property("k2") - .fuzzy_search("Paper_Airpla", 2, false); - let expected_results = vec!["N1->N2"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - #[ignore] - fn test_edges_filters_pg_fuzzy_search() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("k2").fuzzy_search("Paper_", 2, false); - let expected_results = vec!["N1->N2", "N2->N3", "N7->N8"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::PersistentOnly, - ); - - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - - #[test] - fn test_edges_filters_fuzzy_search_prefix_match() { - let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec!["N1->N2", "N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - - let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec!["N1->N2", "N2->N3"]; - assert_filter_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter.clone(), - &expected_results, - TestVariants::EventOnly, - ); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::EventOnly, - ); - } - - #[test] - #[ignore] - fn test_edges_filters_pg_fuzzy_search_prefix_match() { - // TODO: PropertyFilteringNotImplemented for variants persistent_graph, persistent_disk_graph for filter_edges. - let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, true); - let expected_results = vec![ - "N1->N2", "N12->N13", "N13->N14", "N2->N3", "N5->N6", "N7->N8", "N8->N9", - ]; - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - - let filter = EdgeFilter.property("k2").fuzzy_search("Pa", 2, false); - let expected_results = Vec::<&str>::new(); - assert_search_edges_results( - init_graph, - WindowGraphTransformer(6..9), - filter, - &expected_results, - TestVariants::PersistentOnly, - ); - } - } - } -} From dbb899782730c8a08c98a01a78001a3b073aaa8f Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Tue, 19 May 2026 18:06:44 +0100 Subject: [PATCH 4/8] is search broken? --- Cargo.lock | 26 ++++ raphtory-test-utils/Cargo.toml | 3 + raphtory-test-utils/src/assertions.rs | 6 +- raphtory-test-utils/tests/graph_index.rs | 127 ++++++++++++++++ .../views/filter/model/node_filter/mod.rs | 2 +- raphtory/src/search/graph_index.rs | 136 +----------------- raphtory/src/search/node_index.rs | 14 +- 7 files changed, 167 insertions(+), 147 deletions(-) create mode 100644 raphtory-test-utils/tests/graph_index.rs diff --git a/Cargo.lock b/Cargo.lock index afb29f7572..6b08546d51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6555,6 +6555,7 @@ dependencies = [ "raphtory", "raphtory-api", "raphtory-storage", + "raphtory-test-utils", "rayon", "reqwest", "rust-embed", @@ -6631,6 +6632,31 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "raphtory-test-utils" +version = "0.18.0" +dependencies = [ + "ahash", + "arrow", + "bigdecimal", + "chrono", + "db4-storage", + "itertools 0.13.0", + "parquet", + "proptest", + "proptest-derive", + "rand 0.9.4", + "raphtory", + "raphtory-api", + "raphtory-storage", + "rayon", + "serde", + "serde_json", + "tempfile", + "tracing", + "zip", +] + [[package]] name = "raphtory_custom_gql_apis" version = "0.9.3" diff --git a/raphtory-test-utils/Cargo.toml b/raphtory-test-utils/Cargo.toml index d163873fa9..56b74a58a8 100644 --- a/raphtory-test-utils/Cargo.toml +++ b/raphtory-test-utils/Cargo.toml @@ -34,3 +34,6 @@ arrow.workspace = true parquet.workspace = true tracing.workspace = true zip.workspace = true + +[features] +search = ["raphtory/search"] diff --git a/raphtory-test-utils/src/assertions.rs b/raphtory-test-utils/src/assertions.rs index 74f16f78d9..f13b503202 100644 --- a/raphtory-test-utils/src/assertions.rs +++ b/raphtory-test-utils/src/assertions.rs @@ -5,9 +5,7 @@ use raphtory::{ use std::ops::Range; #[cfg(feature = "search")] -pub use crate::db::api::view::SearchableGraphOps; -#[cfg(feature = "search")] -use raphtory::prelude::IndexMutationOps; +use raphtory::prelude::{IndexMutationOps, SearchableGraphOps}; use raphtory::{ db::{ api::view::filter_ops::{EdgeSelect, NodeSelect}, @@ -378,7 +376,7 @@ pub fn assert_search_edges_results( #[track_caller] fn assert_results( init_graph: impl FnOnce(Graph) -> Graph, - pre_transform: impl Fn(&Graph) -> (), + pre_transform: impl Fn(&Graph), transform: impl GraphTransformer, expected: &[&str], variants: Vec, diff --git a/raphtory-test-utils/tests/graph_index.rs b/raphtory-test-utils/tests/graph_index.rs new file mode 100644 index 0000000000..ebd9bdd41b --- /dev/null +++ b/raphtory-test-utils/tests/graph_index.rs @@ -0,0 +1,127 @@ +#[cfg(all(test, feature = "search"))] +mod graph_index_test { + use raphtory::prelude::{AdditionOps, Graph, GraphViewOps, IndexMutationOps}; + + use raphtory::db::graph::views::filter::model::{ + edge_filter::EdgeFilter, node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, + PropertyFilterFactory, + }; + use raphtory_test_utils::assertions::{search_edges, search_nodes}; + + fn init_nodes_graph(graph: Graph) -> Graph { + graph + .add_node(1, 1, [("p1", 1), ("p2", 2)], Some("fire_nation"), None) + .unwrap(); + graph + .add_node(2, 1, [("p6", 6)], Some("fire_nation"), None) + .unwrap(); + graph + .add_node(2, 2, [("p4", 5)], Some("fire_nation"), None) + .unwrap(); + graph + .add_node(3, 3, [("p2", 4), ("p3", 3)], Some("water_tribe"), None) + .unwrap(); + graph + } + + fn init_edges_graph(graph: Graph) -> Graph { + graph + .add_edge(1, 1, 2, [("p1", 1), ("p2", 2)], None) + .unwrap(); + graph.add_edge(2, 1, 2, [("p6", 6)], None).unwrap(); + graph.add_edge(2, 2, 3, [("p4", 5)], None).unwrap(); + graph + .add_edge(3, 3, 4, [("p2", 4), ("p3", 3)], None) + .unwrap(); + graph + } + + #[test] + fn test_if_bulk_load_create_graph_index_is_ok() { + let graph = Graph::new(); + let graph = init_nodes_graph(graph); + + assert_eq!(graph.count_nodes(), 3); + + graph.create_index_in_ram().unwrap(); + } + + #[test] + fn test_if_adding_nodes_to_existing_graph_index_is_ok() { + let graph = Graph::new(); + graph.create_index_in_ram().unwrap(); + + let graph = init_nodes_graph(graph); + + assert_eq!(graph.count_nodes(), 3); + } + + #[test] + fn test_if_adding_edges_to_existing_graph_index_is_ok() { + let graph = Graph::new(); + // Creates graph index + graph.create_index_in_ram().unwrap(); + + let graph = init_edges_graph(graph); + + assert_eq!(graph.count_edges(), 3); + } + + #[test] + fn test_node_metadata_graph_index_is_ok() { + let graph = Graph::new(); + let graph = init_nodes_graph(graph); + graph.create_index_in_ram().unwrap(); + graph.node(1).unwrap().add_metadata([("x", 1u64)]).unwrap(); + + let filter = NodeFilter.metadata("x").eq(1u64); + assert_eq!(search_nodes(&graph, filter.clone()), vec!["1"]); + + graph + .node(1) + .unwrap() + .update_metadata([("x", 2u64)]) + .unwrap(); + let filter = NodeFilter.metadata("x").eq(1u64); + assert_eq!(search_nodes(&graph, filter.clone()), Vec::<&str>::new()); + + graph + .node(1) + .unwrap() + .update_metadata([("x", 2u64)]) + .unwrap(); + let filter = NodeFilter.metadata("x").eq(2u64); + assert_eq!(search_nodes(&graph, filter.clone()), vec!["1"]); + } + + #[test] + fn test_edge_metadata_graph_index_is_ok() { + let graph = Graph::new(); + let graph = init_edges_graph(graph); + graph.create_index_in_ram().unwrap(); + graph + .edge(1, 2) + .unwrap() + .add_metadata([("x", 1u64)], None) + .unwrap(); + + let filter = EdgeFilter.metadata("x").eq(1u64); + assert_eq!(search_edges(&graph, filter.clone()), vec!["1->2"]); + + graph + .edge(1, 2) + .unwrap() + .update_metadata([("x", 2u64)], None) + .unwrap(); + let filter = EdgeFilter.metadata("x").eq(1u64); + assert_eq!(search_edges(&graph, filter.clone()), Vec::<&str>::new()); + + graph + .edge(1, 2) + .unwrap() + .update_metadata([("x", 2u64)], None) + .unwrap(); + let filter = EdgeFilter.metadata("x").eq(2u64); + assert_eq!(search_edges(&graph, filter.clone()), vec!["1->2"]); + } +} diff --git a/raphtory/src/db/graph/views/filter/model/node_filter/mod.rs b/raphtory/src/db/graph/views/filter/model/node_filter/mod.rs index 9ccfa9a0e3..6064c5e102 100644 --- a/raphtory/src/db/graph/views/filter/model/node_filter/mod.rs +++ b/raphtory/src/db/graph/views/filter/model/node_filter/mod.rs @@ -80,7 +80,7 @@ impl NodeFilter { V: NodeStateValue + 'graph, T: Clone + Send + Sync + 'graph, { - Ok(state.bool_col_filter(col)?) + state.bool_col_filter(col) } } diff --git a/raphtory/src/search/graph_index.rs b/raphtory/src/search/graph_index.rs index 630886490b..c0a76e71c2 100644 --- a/raphtory/src/search/graph_index.rs +++ b/raphtory/src/search/graph_index.rs @@ -58,7 +58,7 @@ impl MutableGraphIndex { pub fn update(&self, graph: &GraphStorage, index_spec: IndexSpec) -> Result<(), GraphError> { let mut existing_spec = self.index_spec.write(); - if let Some(diff_spec) = IndexSpec::diff(&*existing_spec, &index_spec) { + if let Some(diff_spec) = IndexSpec::diff(&existing_spec, &index_spec) { let path = get_node_index_path(&self.path); self.index .node_index @@ -71,7 +71,7 @@ impl MutableGraphIndex { // self.index.print()?; - *existing_spec = IndexSpec::union(&*existing_spec, &diff_spec); + *existing_spec = IndexSpec::union(&existing_spec, &diff_spec); } Ok(()) @@ -447,135 +447,3 @@ fn load_indexes(index_path: &Path) -> Result<(Index, IndexSpec), GraphError> { index_spec, )) } - -#[cfg(test)] -mod graph_index_test { - use crate::prelude::{AdditionOps, Graph, GraphViewOps}; - - use crate::db::graph::views::filter::model::{ - edge_filter::EdgeFilter, node_filter::NodeFilter, property_filter::ops::PropertyFilterOps, - PropertyFilterFactory, - }; - #[cfg(feature = "search")] - use crate::{ - db::graph::assertions::{search_edges, search_nodes}, - prelude::IndexMutationOps, - }; - - fn init_nodes_graph(graph: Graph) -> Graph { - graph - .add_node(1, 1, [("p1", 1), ("p2", 2)], Some("fire_nation"), None) - .unwrap(); - graph - .add_node(2, 1, [("p6", 6)], Some("fire_nation"), None) - .unwrap(); - graph - .add_node(2, 2, [("p4", 5)], Some("fire_nation"), None) - .unwrap(); - graph - .add_node(3, 3, [("p2", 4), ("p3", 3)], Some("water_tribe"), None) - .unwrap(); - graph - } - - fn init_edges_graph(graph: Graph) -> Graph { - graph - .add_edge(1, 1, 2, [("p1", 1), ("p2", 2)], None) - .unwrap(); - graph.add_edge(2, 1, 2, [("p6", 6)], None).unwrap(); - graph.add_edge(2, 2, 3, [("p4", 5)], None).unwrap(); - graph - .add_edge(3, 3, 4, [("p2", 4), ("p3", 3)], None) - .unwrap(); - graph - } - - #[test] - fn test_if_bulk_load_create_graph_index_is_ok() { - let graph = Graph::new(); - let graph = init_nodes_graph(graph); - - assert_eq!(graph.count_nodes(), 3); - - graph.create_index_in_ram().unwrap(); - } - - #[test] - fn test_if_adding_nodes_to_existing_graph_index_is_ok() { - let graph = Graph::new(); - graph.create_index_in_ram().unwrap(); - - let graph = init_nodes_graph(graph); - - assert_eq!(graph.count_nodes(), 3); - } - - #[test] - fn test_if_adding_edges_to_existing_graph_index_is_ok() { - let graph = Graph::new(); - // Creates graph index - graph.create_index_in_ram().unwrap(); - - let graph = init_edges_graph(graph); - - assert_eq!(graph.count_edges(), 3); - } - - #[test] - fn test_node_metadata_graph_index_is_ok() { - let graph = Graph::new(); - let graph = init_nodes_graph(graph); - graph.create_index_in_ram().unwrap(); - graph.node(1).unwrap().add_metadata([("x", 1u64)]).unwrap(); - - let filter = NodeFilter.metadata("x").eq(1u64); - assert_eq!(search_nodes(&graph, filter.clone()), vec!["1"]); - - graph - .node(1) - .unwrap() - .update_metadata([("x", 2u64)]) - .unwrap(); - let filter = NodeFilter.metadata("x").eq(1u64); - assert_eq!(search_nodes(&graph, filter.clone()), Vec::<&str>::new()); - - graph - .node(1) - .unwrap() - .update_metadata([("x", 2u64)]) - .unwrap(); - let filter = NodeFilter.metadata("x").eq(2u64); - assert_eq!(search_nodes(&graph, filter.clone()), vec!["1"]); - } - - #[test] - fn test_edge_metadata_graph_index_is_ok() { - let graph = Graph::new(); - let graph = init_edges_graph(graph); - graph.create_index_in_ram().unwrap(); - graph - .edge(1, 2) - .unwrap() - .add_metadata([("x", 1u64)], None) - .unwrap(); - - let filter = EdgeFilter.metadata("x").eq(1u64); - assert_eq!(search_edges(&graph, filter.clone()), vec!["1->2"]); - - graph - .edge(1, 2) - .unwrap() - .update_metadata([("x", 2u64)], None) - .unwrap(); - let filter = EdgeFilter.metadata("x").eq(1u64); - assert_eq!(search_edges(&graph, filter.clone()), Vec::<&str>::new()); - - graph - .edge(1, 2) - .unwrap() - .update_metadata([("x", 2u64)], None) - .unwrap(); - let filter = EdgeFilter.metadata("x").eq(2u64); - assert_eq!(search_edges(&graph, filter.clone()), vec!["1->2"]); - } -} diff --git a/raphtory/src/search/node_index.rs b/raphtory/src/search/node_index.rs index 986fdd77b2..4986ee8a13 100644 --- a/raphtory/src/search/node_index.rs +++ b/raphtory/src/search/node_index.rs @@ -10,7 +10,7 @@ use crate::{ }, }; use ahash::HashSet; -use raphtory_storage::graph::graph::GraphStorage; +use raphtory_storage::graph::{graph::GraphStorage, nodes::node_storage_ops::NodeStorageOps}; use rayon::{iter::IntoParallelIterator, prelude::ParallelIterator}; use std::{ fmt::{Debug, Formatter}, @@ -222,13 +222,11 @@ impl NodeIndex { // Index nodes fields let mut writer = self.entity_index.index.writer(100_000_000)?; - (0..graph.count_nodes()) - .into_par_iter() - .try_for_each(|v_id| { - let node = NodeView::new_internal(graph, VID(v_id)); - self.index_node(node, &writer)?; - Ok::<(), GraphError>(()) - })?; + graph.nodes().par_iter().try_for_each(|node| { + let node = NodeView::new_internal(graph, node.vid()); + self.index_node(node, &writer)?; + Ok::<(), GraphError>(()) + })?; writer.commit()?; Ok(()) From 68a592ec1f072ba0f387dfc16e3cb00e2945c6c0 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Tue, 19 May 2026 18:53:26 +0100 Subject: [PATCH 5/8] move search tests --- raphtory-test-utils/tests/graph_index.rs | 854 ++++++++++++++++++++++ raphtory/src/db/api/storage/storage.rs | 6 +- raphtory/src/db/api/view/graph.rs | 14 + raphtory/src/search/entity_index.rs | 27 +- raphtory/src/search/mod.rs | 857 +---------------------- 5 files changed, 887 insertions(+), 871 deletions(-) diff --git a/raphtory-test-utils/tests/graph_index.rs b/raphtory-test-utils/tests/graph_index.rs index ebd9bdd41b..915cc688d4 100644 --- a/raphtory-test-utils/tests/graph_index.rs +++ b/raphtory-test-utils/tests/graph_index.rs @@ -125,3 +125,857 @@ mod graph_index_test { assert_eq!(search_edges(&graph, filter.clone()), vec!["1->2"]); } } + +#[cfg(all(test, feature = "search"))] +mod test_index { + mod test_index_io { + use raphtory::{ + db::{ + api::view::{internal::InternalStorageOps, ResolvedIndexSpec}, + graph::views::filter::model::{ + node_filter::{ops::NodeFilterOps, NodeFilter}, + TryAsCompositeFilter, + }, + }, + errors::GraphError, + prelude::*, + serialise::GraphFolder, + }; + use raphtory_api::core::{ + entities::properties::prop::Prop, storage::arc_str::ArcStr, + utils::logging::global_info_logger, + }; + use tempfile::TempDir; + + fn temp_storage_path() -> std::path::PathBuf { + tempfile::tempdir().unwrap().path().to_path_buf() + } + + fn init_graph() -> Graph { + let graph = Graph::new(); + + graph + .add_node( + 1, + "Alice", + vec![("p1", Prop::U64(1000u64))], + Some("fire_nation"), + None, + ) + .unwrap(); + graph + } + + fn assert_search_results( + graph: &Graph, + filter: &T, + expected: Vec<&str>, + ) { + let res = graph + .search_nodes(filter.clone(), 2, 0) + .unwrap() + .into_iter() + .map(|n| n.name()) + .collect::>(); + assert_eq!(res, expected); + } + + #[test] + fn test_create_no_index_persist_no_index_on_encode_load_no_index_on_decode() { + // No index persisted since it was never created + let graph = init_graph(); + + let filter = NodeFilter::name().eq("Alice"); + assert_search_results(&graph, &filter, vec!["Alice"]); + + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + let graph = Graph::decode(path).unwrap(); + let is_indexed = graph.get_storage().unwrap().is_indexed(); + assert!(!is_indexed); + } + + #[test] + fn test_create_index_persist_index_on_encode_load_index_on_decode() { + let graph = init_graph(); + + // Created index + graph.create_index().unwrap(); + + let filter = NodeFilter::name().eq("Alice"); + assert_search_results(&graph, &filter, vec!["Alice"]); + + // Persisted both graph and index + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + // Loaded index that was persisted + let graph = Graph::decode(path).unwrap(); + let is_indexed = graph.get_storage().unwrap().is_indexed(); + assert!(is_indexed); + + assert_search_results(&graph, &filter, vec!["Alice"]); + } + + #[test] + fn test_encoding_graph_twice_to_same_storage_path_fails() { + let graph = init_graph(); + graph.create_index().unwrap(); + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + let result = graph.encode(path); + + match result { + Err(GraphError::NonEmptyGraphFolder(err_path)) => { + assert_eq!(path, err_path); + } + Ok(_) => panic!("Expected error on second encode, got Ok"), + Err(e) => panic!("Unexpected error type: {:?}", e), + } + } + + #[test] + fn test_create_index_persist_index_on_encode_update_index_load_persisted_index_on_decode() { + let graph = init_graph(); + + // Created index + graph.create_index().unwrap(); + + let filter1 = NodeFilter::name().eq("Alice"); + assert_search_results(&graph, &filter1, vec!["Alice"]); + + // Persisted both graph and index + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + // Updated both graph and index + graph + .add_node( + 2, + "Tommy", + vec![("p1", Prop::U64(5u64))], + Some("water_tribe"), + None, + ) + .unwrap(); + let filter2 = NodeFilter::name().eq("Tommy"); + assert_search_results(&graph, &filter2, vec!["Tommy"]); + + // Loaded index that was persisted + let graph = Graph::decode(path).unwrap(); + let is_indexed = graph.get_storage().unwrap().is_indexed(); + assert!(is_indexed); + assert_search_results(&graph, &filter1, vec!["Alice"]); + assert_search_results(&graph, &filter2, Vec::<&str>::new()); + + // Updating and encode the graph and index should decode the updated the graph as well as index + // So far we have the index that was created and persisted for the first time + graph + .add_node( + 2, + "Tommy", + vec![("p1", Prop::U64(5u64))], + Some("water_tribe"), + None, + ) + .unwrap(); + let filter2 = NodeFilter::name().eq("Tommy"); + assert_search_results(&graph, &filter2, vec!["Tommy"]); + + // Should persist the updated graph and index + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + // Should load the updated graph and index + let storage_path = path.parent().unwrap().to_path_buf(); + let graph = Graph::decode(path).unwrap(); + let is_indexed = graph.get_storage().unwrap().is_indexed(); + assert!(is_indexed); + assert_search_results(&graph, &filter1, vec!["Alice"]); + assert_search_results(&graph, &filter2, vec!["Tommy"]); + } + + #[test] + fn test_zip_encode_decode_index() { + let graph = init_graph(); + graph.create_index().unwrap(); + let tmp_dir = TempDir::new().unwrap(); + let zip_path = tmp_dir.path().join("graph.zip"); + let folder = GraphFolder::new_as_zip(zip_path); + graph.encode(&folder).unwrap(); + + let storage_path = tmp_dir.path().to_path_buf(); + let graph = Graph::decode(&folder).unwrap(); + let node = graph.node("Alice").unwrap(); + let node_type = node.node_type(); + assert_eq!(node_type, Some(ArcStr::from("fire_nation"))); + + let filter = NodeFilter::name().eq("Alice"); + assert_search_results(&graph, &filter, vec!["Alice"]); + } + + #[test] + fn test_encoding_graph_twice_to_same_storage_path_fails_zip() { + let graph = init_graph(); + graph.create_index().unwrap(); + let tmp_dir = TempDir::new().unwrap(); + let zip_path = tmp_dir.path().join("graph.zip"); + let folder = GraphFolder::new_as_zip(&zip_path); + graph.encode(&folder).unwrap(); + graph + .add_node(1, "Ozai", [("prop", 1)], Some("fire_nation"), None) + .unwrap(); + let result = graph.encode(folder); + match result { + Err(GraphError::IOError { source, .. }) => { + assert!( + format!("{source}").to_lowercase().contains("file exists"), + "{}", + source + ); + } + Ok(_) => panic!("Expected error on second encode, got Ok"), + Err(e) => panic!("Unexpected error type: {:?}", e), + } + } + + #[test] + fn test_immutable_graph_index_persistence() { + let graph = init_graph(); + graph.create_index().unwrap(); + + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + // This gives us immutable index + let graph = Graph::decode(path).unwrap(); + + // This tests that we are able to persist the immutable index + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + let graph = Graph::decode(path).unwrap(); + let filter1 = NodeFilter::name().eq("Alice"); + assert_search_results(&graph, &filter1, vec!["Alice"]); + } + + #[test] + fn test_mutable_graph_index_persistence() { + let graph = init_graph(); + graph.create_index().unwrap(); + + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + // This gives us immutable index + let graph = Graph::decode(path).unwrap(); + + // This converts immutable index to mutable index + graph + .add_node(1, "Ozai", [("prop", 1)], Some("fire_nation"), None) + .unwrap(); + + // This tests that we are able to persist the mutable index + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + let graph = Graph::decode(path).unwrap(); + let filter = NodeFilter::name().eq("Ozai"); + assert_search_results(&graph, &filter, vec!["Ozai"]); + } + + #[test] + fn test_loading_zip_index_creates_mutable_index() { + let graph = init_graph(); + graph.create_index().unwrap(); + let tmp_dir = TempDir::new().unwrap(); + let zip_path = tmp_dir.path().join("graph.zip"); + let folder = GraphFolder::new_as_zip(&zip_path); + graph.encode(&folder).unwrap(); + + let graph = Graph::decode(&folder).unwrap(); + let immutable = graph + .get_storage() + .unwrap() + .index() + .read_recursive() + .is_immutable(); + assert! {!immutable}; + } + + #[test] + fn test_loading_index_creates_immutable_index() { + let graph = init_graph(); + graph.create_index().unwrap(); + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + let graph = Graph::decode(path).unwrap(); + let immutable = graph + .get_storage() + .unwrap() + .index() + .read_recursive() + .is_immutable(); + assert! {immutable}; + } + + #[test] + fn test_create_index_in_ram() { + global_info_logger(); + + let graph = init_graph(); + graph.create_index_in_ram().unwrap(); + + let filter = NodeFilter::name().eq("Alice"); + assert_search_results(&graph, &filter, vec!["Alice"]); + + let binding = TempDir::new().unwrap(); + let path = binding.path(); + graph.encode(path).unwrap(); + + let graph = Graph::decode(path).unwrap(); + let is_indexed = graph.get_storage().unwrap().is_indexed(); + assert!(!is_indexed); + + assert_search_results(&graph, &filter, vec!["Alice"]); + } + + #[test] + #[ignore] + fn test_too_many_open_files_graph_index() { + use TempDir; + + let mut graphs = vec![]; + + for i in 0..1000 { + let graph = init_graph(); + if let Err(e) = graph.create_index() { + match &e { + GraphError::IndexError { source } => { + panic!("Hit file descriptor limit after {} graphs. {:?}", 0, source); + } + other => { + panic!("Unexpected GraphError: {:?}", other); + } + } + } + graphs.push(graph); + } + } + + #[test] + fn test_graph_index_creation_with_too_many_properties() { + let graph = init_graph(); + let props: Vec<(String, Prop)> = (1..=100) + .map(|i| (format!("p{i}"), Prop::U64(i as u64))) + .collect(); + graph.node("Alice").unwrap().add_metadata(props).unwrap(); + + if let Err(e) = graph.create_index() { + match &e { + GraphError::IndexError { source } => { + panic!("Hit file descriptor limit after {} graphs. {:?}", 0, source); + } + other => { + panic!("Unexpected GraphError: {:?}", other); + } + } + } + } + + #[test] + // No new const prop index created because when index were created + // these properties did not exist. + fn test_graph_index_creation_for_incremental_node_update_no_new_prop_indexed() { + let graph = init_graph(); + graph.create_index().unwrap(); + let props: Vec<(String, Prop)> = (1..=100) + .map(|i| (format!("p{i}"), Prop::U64(i as u64))) + .collect(); + graph.node("Alice").unwrap().add_metadata(props).unwrap(); + + let tmp_dir = TempDir::new().unwrap(); + let path = tmp_dir.path().to_path_buf(); + graph.encode(&path).unwrap(); + let graph = Graph::decode(&path).unwrap(); + + let spec = graph.get_index_spec().unwrap().props(&graph); + assert_eq!( + spec, + ResolvedIndexSpec { + node_properties: vec!["p1".to_string()], + node_metadata: vec![], + edge_metadata: vec![], + edge_properties: vec![] + } + ); + } + } + + mod test_index_spec { + use raphtory::prelude::SearchableGraphOps; + use raphtory::{ + db::{ + api::view::{IndexSpec, IndexSpecBuilder}, + graph::views::filter::model::{ + edge_filter::EdgeFilter, node_filter::NodeFilter, + property_filter::ops::PropertyFilterOps, ComposableFilter, + PropertyFilterFactory, TemporalPropertyFilterFactory, + }, + }, + errors::GraphError, + prelude::{AdditionOps, Graph, IndexMutationOps, StableDecode}, + serialise::{GraphFolder, StableEncode}, + }; + use raphtory_test_utils::assertions::{search_edges, search_nodes}; + use tempfile::{tempdir, TempDir}; + + fn temp_storage_path() -> std::path::PathBuf { + tempfile::tempdir().unwrap().path().to_path_buf() + } + + fn init_graph() -> Graph { + let graph = Graph::new(); + + let nodes = vec![ + ( + 1, + "pometry", + [("p1", 5u64), ("p2", 50u64)], + Some("fire_nation"), + [("x", true)], + ), + ( + 1, + "raphtory", + [("p1", 10u64), ("p2", 100u64)], + Some("water_tribe"), + [("y", false)], + ), + ]; + + for (time, name, props, group, metadata) in nodes { + let node = graph.add_node(time, name, props, group, None).unwrap(); + node.add_metadata(metadata).unwrap(); + } + + let edges = vec![ + ( + 1, + "pometry", + "raphtory", + [("e_p1", 3.2f64), ("e_p2", 10f64)], + None, + [("e_x", true)], + ), + ( + 1, + "raphtory", + "pometry", + [("e_p1", 4.0f64), ("e_p2", 20f64)], + None, + [("e_y", false)], + ), + ]; + + for (time, src, dst, props, label, metadata) in edges { + let edge = graph.add_edge(time, src, dst, props, label).unwrap(); + edge.add_metadata(metadata, label).unwrap(); + } + + graph + } + + #[test] + #[ignore = "TODO: #2372"] + fn test_with_all_props_index_spec() { + let graph = init_graph(); + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_all_node_properties_and_metadata() + .with_all_edge_properties_and_metadata() + .build(); + assert_eq!( + index_spec.props(&graph).to_vec(), + vec![ + vec!["x", "y"], + vec!["p1", "p2"], + vec!["e_x", "e_y"], + vec!["e_p1", "e_p2"] + ] + ); + graph.create_index_in_ram_with_spec(index_spec).unwrap(); + + let filter = NodeFilter + .property("p1") + .eq(5u64) + .and(NodeFilter.metadata("x").eq(true)); + let results = search_nodes(&graph, filter); + assert_eq!(results, vec!["pometry"]); + + let filter = EdgeFilter + .property("e_p1") + .lt(5f64) + .and(EdgeFilter.metadata("e_y").eq(false)); + let results = search_edges(&graph, filter); + assert_eq!(results, vec!["raphtory->pometry"]); + } + + #[test] + #[ignore = "TODO: #2372"] + fn test_with_selected_props_index_spec() { + let graph = init_graph(); + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_node_metadata(vec!["y"]) + .unwrap() + .with_node_properties(vec!["p1"]) + .unwrap() + .with_edge_metadata(vec!["e_y"]) + .unwrap() + .with_edge_properties(vec!["e_p1"]) + .unwrap() + .build(); + assert_eq!( + index_spec.props(&graph).to_vec(), + vec![vec!["y"], vec!["p1"], vec!["e_y"], vec!["e_p1"]] + ); + graph.create_index_in_ram_with_spec(index_spec).unwrap(); + + let filter = NodeFilter + .property("p1") + .eq(5u64) + .or(NodeFilter.metadata("y").eq(false)); + let results = search_nodes(&graph, filter); + assert_eq!(results, vec!["pometry", "raphtory"]); + + let filter = NodeFilter.metadata("y").eq(false); + let results = search_nodes(&graph, filter); + assert_eq!(results, vec!["raphtory"]); + + let filter = EdgeFilter + .property("e_p1") + .lt(5f64) + .or(EdgeFilter.metadata("e_y").eq(false)); + let results = search_edges(&graph, filter); + assert_eq!(results, vec!["pometry->raphtory", "raphtory->pometry"]); + } + + #[test] + fn test_with_invalid_property_returns_error() { + let graph = init_graph(); + let result = IndexSpecBuilder::new(graph.clone()).with_node_metadata(["xyz"]); + + assert!(matches!(result, Err(GraphError::PropertyMissingError(p)) if p == "xyz")); + } + + #[test] + fn test_build_empty_spec_by_default() { + let graph = init_graph(); + let index_spec = IndexSpecBuilder::new(graph.clone()).build(); + + assert!(index_spec.node_metadata().is_empty()); + assert!(index_spec.node_properties().is_empty()); + assert!(index_spec.edge_metadata().is_empty()); + assert!(index_spec.edge_properties().is_empty()); + + graph.create_index_in_ram_with_spec(index_spec).unwrap(); + + let filter = NodeFilter + .property("p1") + .eq(5u64) + .and(NodeFilter.metadata("x").eq(true)); + let results = search_nodes(&graph, filter); + assert_eq!(results, vec!["pometry"]); + + let filter = EdgeFilter + .property("e_p1") + .lt(5f64) + .or(EdgeFilter.metadata("e_y").eq(false)); + let results = search_edges(&graph, filter); + assert_eq!(results, vec!["pometry->raphtory", "raphtory->pometry"]); + } + + #[test] + #[ignore = "TODO: #2372"] + fn test_mixed_node_and_edge_props_index_spec() { + let graph = init_graph(); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_node_metadata(vec!["x"]) + .unwrap() + .with_all_node_properties() + .with_all_edge_properties_and_metadata() + .build(); + assert_eq!( + index_spec.props(&graph).to_vec(), + vec![ + vec!["x"], + vec!["p1", "p2"], + vec!["e_x", "e_y"], + vec!["e_p1", "e_p2"] + ] + ); + + graph.create_index_in_ram_with_spec(index_spec).unwrap(); + + let filter = NodeFilter + .property("p1") + .eq(5u64) + .or(NodeFilter.metadata("y").eq(false)); + let results = search_nodes(&graph, filter); + assert_eq!(results, vec!["pometry", "raphtory"]); + + let filter = EdgeFilter + .property("e_p1") + .lt(5f64) + .or(EdgeFilter.metadata("e_y").eq(false)); + let results = search_edges(&graph, filter); + assert_eq!(results, vec!["pometry->raphtory", "raphtory->pometry"]); + } + + #[test] + fn test_get_index_spec_newly_created_index() { + let graph = init_graph(); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_node_metadata(vec!["x"]) + .unwrap() + .with_all_node_properties() + .with_all_edge_properties_and_metadata() + .build(); + + graph + .create_index_in_ram_with_spec(index_spec.clone()) + .unwrap(); + + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + } + + #[test] + #[ignore = "TODO: #2372"] + fn test_get_index_spec_updated_index() { + let graph = init_graph(); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_edge_metadata(vec!["e_y"]) + .unwrap() + .build(); + graph.create_index_with_spec(index_spec.clone()).unwrap(); + + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + let results = search_nodes(&graph, NodeFilter.metadata("y").eq(false)); + assert_eq!(results, vec!["raphtory"]); + let results = search_edges(&graph, EdgeFilter.metadata("e_y").eq(false)); + assert_eq!(results, vec!["raphtory->pometry"]); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_node_metadata(vec!["y"]) + .unwrap() + .with_node_properties(vec!["p2"]) + .unwrap() + .with_edge_metadata(vec!["e_y"]) + .unwrap() + .build(); + graph.create_index_with_spec(index_spec.clone()).unwrap(); + + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + let results = search_nodes(&graph, NodeFilter.metadata("y").eq(false)); + assert_eq!(results, vec!["raphtory"]); + let results = search_edges(&graph, EdgeFilter.metadata("e_y").eq(false)); + assert_eq!(results, vec!["raphtory->pometry"]); + } + + #[test] + #[ignore = "TODO: #2372"] + fn test_get_index_spec_updated_index_persisted_and_loaded() { + let graph = init_graph(); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_edge_metadata(vec!["e_y"]) + .unwrap() + .build(); + graph.create_index_with_spec(index_spec.clone()).unwrap(); + + let tmp_graph_dir = tempdir().unwrap(); + let path = tmp_graph_dir.path().to_path_buf(); + graph.encode(&path).unwrap(); + let graph = Graph::decode(&path).unwrap(); + + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + let results = search_nodes(&graph, NodeFilter.metadata("y").eq(false)); + assert_eq!(results, vec!["raphtory"]); + let results = search_edges(&graph, EdgeFilter.metadata("e_y").eq(false)); + assert_eq!(results, vec!["raphtory->pometry"]); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_node_metadata(vec!["y"]) + .unwrap() + .with_node_properties(vec!["p2"]) + .unwrap() + .with_edge_metadata(vec!["e_y"]) + .unwrap() + .build(); + graph.create_index_with_spec(index_spec.clone()).unwrap(); + let tmp_graph_dir = tempdir().unwrap(); + let path = tmp_graph_dir.path().to_path_buf(); + graph.encode(path.clone()).unwrap(); + let graph = Graph::decode(&path).unwrap(); + + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + let results = search_nodes(&graph, NodeFilter.metadata("y").eq(false)); + assert_eq!(results, vec!["raphtory"]); + let results = search_edges(&graph, EdgeFilter.metadata("e_y").eq(false)); + assert_eq!(results, vec!["raphtory->pometry"]); + } + + #[test] + fn test_get_index_spec_loaded_index() { + let graph = init_graph(); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_node_metadata(vec!["y"]) + .unwrap() + .with_node_properties(vec!["p2"]) + .unwrap() + .with_edge_metadata(vec!["e_y"]) + .unwrap() + .with_edge_properties(vec!["e_p2"]) + .unwrap() + .build(); + + graph.create_index_with_spec(index_spec.clone()).unwrap(); + let tmp_graph_dir = tempdir().unwrap(); + let path = tmp_graph_dir.path().to_path_buf(); + graph.encode(path.clone()).unwrap(); + + let graph = Graph::decode(&path).unwrap(); + let index_spec2 = graph.get_index_spec().unwrap(); + + assert_eq!(index_spec, index_spec2); + } + + #[test] + fn test_get_index_spec_loaded_index_zip() { + let graph = init_graph(); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_node_metadata(vec!["y"]) + .unwrap() + .with_node_properties(vec!["p2"]) + .unwrap() + .with_edge_metadata(vec!["e_y"]) + .unwrap() + .build(); + graph.create_index_with_spec(index_spec.clone()).unwrap(); + + let binding = TempDir::new().unwrap(); + let path = binding.path(); + let folder = GraphFolder::new_as_zip(path); + graph.encode(folder).unwrap(); + + let graph = Graph::decode(path).unwrap(); + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + } + + #[test] + fn test_no_new_node_prop_index_created_via_update_apis() { + run_node_index_test(|graph, index_spec| { + graph.create_index_with_spec(index_spec.clone()) + }); + + run_node_index_test(|graph, index_spec| { + graph.create_index_in_ram_with_spec(index_spec.clone()) + }); + } + + #[test] + fn test_no_new_edge_prop_index_created_via_update_apis() { + run_edge_index_test(|graph, index_spec| { + graph.create_index_with_spec(index_spec.clone()) + }); + + run_edge_index_test(|graph, index_spec| { + graph.create_index_in_ram_with_spec(index_spec.clone()) + }); + } + + fn run_node_index_test(create_index_fn: F) + where + F: Fn(&Graph, IndexSpec) -> Result<(), GraphError>, + { + let graph = init_graph(); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_node_metadata(vec!["y"]) + .unwrap() + .with_node_properties(vec!["p1"]) + .unwrap() + .build(); + create_index_fn(&graph, index_spec.clone()).unwrap(); + + let filter = NodeFilter.property("p2").temporal().last().eq(50u64); + assert_eq!(search_nodes(&graph, filter.clone()), vec!["pometry"]); + + let node = graph + .add_node(1, "shivam", [("p1", 100u64)], Some("fire_nation"), None) + .unwrap(); + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + + let filter = NodeFilter.property("p1").temporal().last().eq(100u64); + assert_eq!(search_nodes(&graph, filter.clone()), vec!["shivam"]); + + node.add_metadata([("z", true)]).unwrap(); + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + let filter = NodeFilter.metadata("z").eq(true); + assert_eq!(search_nodes(&graph, filter.clone()), vec!["shivam"]); + + node.update_metadata([("z", false)]).unwrap(); + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + let filter = NodeFilter.metadata("z").eq(false); + assert_eq!(search_nodes(&graph, filter.clone()), vec!["shivam"]); + } + + fn run_edge_index_test(create_index_fn: F) + where + F: Fn(&Graph, IndexSpec) -> Result<(), GraphError>, + { + let graph = init_graph(); + + let index_spec = IndexSpecBuilder::new(graph.clone()) + .with_node_metadata(vec!["y"]) + .unwrap() + .with_node_properties(vec!["p2"]) + .unwrap() + .build(); + create_index_fn(&graph, index_spec.clone()).unwrap(); + + let edge = graph + .add_edge(1, "shivam", "kapoor", [("p1", 100u64)], None) + .unwrap(); + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + let filter = EdgeFilter.property("p1").temporal().last().eq(100u64); + assert_eq!(search_edges(&graph, filter.clone()), vec!["shivam->kapoor"]); + + edge.add_metadata([("z", true)], None).unwrap(); + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + let filter = EdgeFilter.metadata("z").eq(true); + assert_eq!(search_edges(&graph, filter.clone()), vec!["shivam->kapoor"]); + + edge.update_metadata([("z", false)], None).unwrap(); + assert_eq!(index_spec, graph.get_index_spec().unwrap()); + let filter = EdgeFilter.metadata("z").eq(false); + assert_eq!(search_edges(&graph, filter.clone()), vec!["shivam->kapoor"]); + } + } +} diff --git a/raphtory/src/db/api/storage/storage.rs b/raphtory/src/db/api/storage/storage.rs index 273acd3589..92bc69d083 100644 --- a/raphtory/src/db/api/storage/storage.rs +++ b/raphtory/src/db/api/storage/storage.rs @@ -102,6 +102,10 @@ impl Storage { } } + #[cfg(feature = "search")] + pub fn index(&self) -> &RwLock { + &self.index + } pub(crate) fn new_at_path(path: impl AsRef) -> Result { let config = Config::default(); let ext = Extension::new(config, Some(path.as_ref()))?; @@ -326,7 +330,7 @@ impl Storage { &self.index } - pub(crate) fn is_indexed(&self) -> bool { + pub fn is_indexed(&self) -> bool { self.index.read_recursive().is_indexed() } diff --git a/raphtory/src/db/api/view/graph.rs b/raphtory/src/db/api/view/graph.rs index 196c3c7a8b..666079e3cc 100644 --- a/raphtory/src/db/api/view/graph.rs +++ b/raphtory/src/db/api/view/graph.rs @@ -936,6 +936,20 @@ impl IndexSpec { } } + pub fn node_metadata(&self) -> &HashSet { + &self.node_metadata + } + + pub fn node_properties(&self) -> &HashSet { + &self.node_properties + } + pub fn edge_metadata(&self) -> &HashSet { + &self.edge_metadata + } + pub fn edge_properties(&self) -> &HashSet { + &self.edge_properties + } + pub fn union(existing: &IndexSpec, other: &IndexSpec) -> IndexSpec { fn union_props(a: &HashSet, b: &HashSet) -> HashSet { a.union(b).copied().collect() diff --git a/raphtory/src/search/entity_index.rs b/raphtory/src/search/entity_index.rs index 506c035ccd..c0fd817251 100644 --- a/raphtory/src/search/entity_index.rs +++ b/raphtory/src/search/entity_index.rs @@ -167,21 +167,20 @@ impl EntityIndex { let indexes = self.temporal_property_indexes.read_recursive(); if let Some(prop_index) = &indexes[prop_id] { let mut writer = prop_index.index.writer(50_000_000)?; - (0..graph.count_nodes()) - .into_par_iter() - .try_for_each(|v_id| { - let node = graph.core_node(VID(v_id)); - for (t, prop_value) in node.tprop(prop_id).iter() { - let prop_doc = prop_index.create_node_temporal_property_document( - t.into(), - v_id as u64, - &prop_value, - )?; - writer.add_document(prop_doc)?; - } + graph.nodes().par_iter().try_for_each(|node| { + let v_id = node.vid(); + let node = graph.core_node(v_id); + for (t, prop_value) in node.tprop(prop_id).iter() { + let prop_doc = prop_index.create_node_temporal_property_document( + t, + v_id.0 as u64, + &prop_value, + )?; + writer.add_document(prop_doc)?; + } - Ok::<(), GraphError>(()) - })?; + Ok::<(), GraphError>(()) + })?; writer.commit()?; } diff --git a/raphtory/src/search/mod.rs b/raphtory/src/search/mod.rs index 5611df1392..1f8e1e649a 100644 --- a/raphtory/src/search/mod.rs +++ b/raphtory/src/search/mod.rs @@ -180,859 +180,4 @@ pub(crate) fn fallback_filter_exploded_edges( } #[cfg(test)] -mod test_index { - #[cfg(feature = "search")] - mod test_index_io { - use crate::{ - db::{ - api::view::{internal::InternalStorageOps, ResolvedIndexSpec}, - graph::views::filter::model::{ - node_filter::{ops::NodeFilterOps, NodeFilter}, - TryAsCompositeFilter, - }, - }, - errors::GraphError, - prelude::*, - serialise::GraphFolder, - }; - use raphtory_api::core::{ - entities::properties::prop::Prop, storage::arc_str::ArcStr, - utils::logging::global_info_logger, - }; - use tempfile::TempDir; - - fn temp_storage_path() -> std::path::PathBuf { - tempfile::tempdir().unwrap().path().to_path_buf() - } - - fn init_graph() -> Graph { - let graph = Graph::new(); - - graph - .add_node( - 1, - "Alice", - vec![("p1", Prop::U64(1000u64))], - Some("fire_nation"), - None, - ) - .unwrap(); - graph - } - - fn assert_search_results( - graph: &Graph, - filter: &T, - expected: Vec<&str>, - ) { - let res = graph - .search_nodes(filter.clone(), 2, 0) - .unwrap() - .into_iter() - .map(|n| n.name()) - .collect::>(); - assert_eq!(res, expected); - } - - #[test] - fn test_create_no_index_persist_no_index_on_encode_load_no_index_on_decode() { - // No index persisted since it was never created - let graph = init_graph(); - - let filter = NodeFilter::name().eq("Alice"); - assert_search_results(&graph, &filter, vec!["Alice"]); - - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - let graph = Graph::decode(path).unwrap(); - let is_indexed = graph.get_storage().unwrap().is_indexed(); - assert!(!is_indexed); - } - - #[test] - fn test_create_index_persist_index_on_encode_load_index_on_decode() { - let graph = init_graph(); - - // Created index - graph.create_index().unwrap(); - - let filter = NodeFilter::name().eq("Alice"); - assert_search_results(&graph, &filter, vec!["Alice"]); - - // Persisted both graph and index - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - // Loaded index that was persisted - let graph = Graph::decode(path).unwrap(); - let is_indexed = graph.get_storage().unwrap().is_indexed(); - assert!(is_indexed); - - assert_search_results(&graph, &filter, vec!["Alice"]); - } - - #[test] - fn test_encoding_graph_twice_to_same_storage_path_fails() { - let graph = init_graph(); - graph.create_index().unwrap(); - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - let result = graph.encode(path); - - match result { - Err(GraphError::NonEmptyGraphFolder(err_path)) => { - assert_eq!(path, err_path); - } - Ok(_) => panic!("Expected error on second encode, got Ok"), - Err(e) => panic!("Unexpected error type: {:?}", e), - } - } - - #[test] - fn test_create_index_persist_index_on_encode_update_index_load_persisted_index_on_decode() { - let graph = init_graph(); - - // Created index - graph.create_index().unwrap(); - - let filter1 = NodeFilter::name().eq("Alice"); - assert_search_results(&graph, &filter1, vec!["Alice"]); - - // Persisted both graph and index - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - // Updated both graph and index - graph - .add_node( - 2, - "Tommy", - vec![("p1", Prop::U64(5u64))], - Some("water_tribe"), - None, - ) - .unwrap(); - let filter2 = NodeFilter::name().eq("Tommy"); - assert_search_results(&graph, &filter2, vec!["Tommy"]); - - // Loaded index that was persisted - let graph = Graph::decode(path).unwrap(); - let is_indexed = graph.get_storage().unwrap().is_indexed(); - assert!(is_indexed); - assert_search_results(&graph, &filter1, vec!["Alice"]); - assert_search_results(&graph, &filter2, Vec::<&str>::new()); - - // Updating and encode the graph and index should decode the updated the graph as well as index - // So far we have the index that was created and persisted for the first time - graph - .add_node( - 2, - "Tommy", - vec![("p1", Prop::U64(5u64))], - Some("water_tribe"), - None, - ) - .unwrap(); - let filter2 = NodeFilter::name().eq("Tommy"); - assert_search_results(&graph, &filter2, vec!["Tommy"]); - - // Should persist the updated graph and index - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - // Should load the updated graph and index - let storage_path = path.parent().unwrap().to_path_buf(); - let graph = Graph::decode(path).unwrap(); - let is_indexed = graph.get_storage().unwrap().is_indexed(); - assert!(is_indexed); - assert_search_results(&graph, &filter1, vec!["Alice"]); - assert_search_results(&graph, &filter2, vec!["Tommy"]); - } - - #[test] - fn test_zip_encode_decode_index() { - let graph = init_graph(); - graph.create_index().unwrap(); - let tmp_dir = TempDir::new().unwrap(); - let zip_path = tmp_dir.path().join("graph.zip"); - let folder = GraphFolder::new_as_zip(zip_path); - graph.encode(&folder).unwrap(); - - let storage_path = tmp_dir.path().to_path_buf(); - let graph = Graph::decode(&folder).unwrap(); - let node = graph.node("Alice").unwrap(); - let node_type = node.node_type(); - assert_eq!(node_type, Some(ArcStr::from("fire_nation"))); - - let filter = NodeFilter::name().eq("Alice"); - assert_search_results(&graph, &filter, vec!["Alice"]); - } - - #[test] - fn test_encoding_graph_twice_to_same_storage_path_fails_zip() { - let graph = init_graph(); - graph.create_index().unwrap(); - let tmp_dir = TempDir::new().unwrap(); - let zip_path = tmp_dir.path().join("graph.zip"); - let folder = GraphFolder::new_as_zip(&zip_path); - graph.encode(&folder).unwrap(); - graph - .add_node(1, "Ozai", [("prop", 1)], Some("fire_nation"), None) - .unwrap(); - let result = graph.encode(folder); - match result { - Err(GraphError::IOError { source, .. }) => { - assert!( - format!("{source}").to_lowercase().contains("file exists"), - "{}", - source - ); - } - Ok(_) => panic!("Expected error on second encode, got Ok"), - Err(e) => panic!("Unexpected error type: {:?}", e), - } - } - - #[test] - fn test_immutable_graph_index_persistence() { - let graph = init_graph(); - graph.create_index().unwrap(); - - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - // This gives us immutable index - let graph = Graph::decode(path).unwrap(); - - // This tests that we are able to persist the immutable index - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - let graph = Graph::decode(path).unwrap(); - let filter1 = NodeFilter::name().eq("Alice"); - assert_search_results(&graph, &filter1, vec!["Alice"]); - } - - #[test] - fn test_mutable_graph_index_persistence() { - let graph = init_graph(); - graph.create_index().unwrap(); - - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - // This gives us immutable index - let graph = Graph::decode(path).unwrap(); - - // This converts immutable index to mutable index - graph - .add_node(1, "Ozai", [("prop", 1)], Some("fire_nation"), None) - .unwrap(); - - // This tests that we are able to persist the mutable index - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - let graph = Graph::decode(path).unwrap(); - let filter = NodeFilter::name().eq("Ozai"); - assert_search_results(&graph, &filter, vec!["Ozai"]); - } - - #[test] - fn test_loading_zip_index_creates_mutable_index() { - let graph = init_graph(); - graph.create_index().unwrap(); - let tmp_dir = TempDir::new().unwrap(); - let zip_path = tmp_dir.path().join("graph.zip"); - let folder = GraphFolder::new_as_zip(&zip_path); - graph.encode(&folder).unwrap(); - - let graph = Graph::decode(&folder).unwrap(); - let immutable = graph - .get_storage() - .unwrap() - .index - .read_recursive() - .is_immutable(); - assert! {!immutable}; - } - - #[test] - fn test_loading_index_creates_immutable_index() { - let graph = init_graph(); - graph.create_index().unwrap(); - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - let graph = Graph::decode(path).unwrap(); - let immutable = graph - .get_storage() - .unwrap() - .index - .read_recursive() - .is_immutable(); - assert! {immutable}; - } - - #[test] - fn test_create_index_in_ram() { - global_info_logger(); - - let graph = init_graph(); - graph.create_index_in_ram().unwrap(); - - let filter = NodeFilter::name().eq("Alice"); - assert_search_results(&graph, &filter, vec!["Alice"]); - - let binding = TempDir::new().unwrap(); - let path = binding.path(); - graph.encode(path).unwrap(); - - let graph = Graph::decode(path).unwrap(); - let is_indexed = graph.get_storage().unwrap().is_indexed(); - assert!(!is_indexed); - - assert_search_results(&graph, &filter, vec!["Alice"]); - } - - #[test] - #[ignore] - fn test_too_many_open_files_graph_index() { - use TempDir; - - let mut graphs = vec![]; - - for i in 0..1000 { - let graph = init_graph(); - if let Err(e) = graph.create_index() { - match &e { - GraphError::IndexError { source } => { - panic!("Hit file descriptor limit after {} graphs. {:?}", 0, source); - } - other => { - panic!("Unexpected GraphError: {:?}", other); - } - } - } - graphs.push(graph); - } - } - - #[test] - fn test_graph_index_creation_with_too_many_properties() { - let graph = init_graph(); - let props: Vec<(String, Prop)> = (1..=100) - .map(|i| (format!("p{i}"), Prop::U64(i as u64))) - .collect(); - graph.node("Alice").unwrap().add_metadata(props).unwrap(); - - if let Err(e) = graph.create_index() { - match &e { - GraphError::IndexError { source } => { - panic!("Hit file descriptor limit after {} graphs. {:?}", 0, source); - } - other => { - panic!("Unexpected GraphError: {:?}", other); - } - } - } - } - - #[test] - // No new const prop index created because when index were created - // these properties did not exist. - fn test_graph_index_creation_for_incremental_node_update_no_new_prop_indexed() { - let graph = init_graph(); - graph.create_index().unwrap(); - let props: Vec<(String, Prop)> = (1..=100) - .map(|i| (format!("p{i}"), Prop::U64(i as u64))) - .collect(); - graph.node("Alice").unwrap().add_metadata(props).unwrap(); - - let tmp_dir = TempDir::new().unwrap(); - let path = tmp_dir.path().to_path_buf(); - graph.encode(&path).unwrap(); - let graph = Graph::decode(&path).unwrap(); - - let spec = graph.get_index_spec().unwrap().props(&graph); - assert_eq!( - spec, - ResolvedIndexSpec { - node_properties: vec!["p1".to_string()], - node_metadata: vec![], - edge_metadata: vec![], - edge_properties: vec![] - } - ); - } - } - - mod test_index_spec { - #[cfg(feature = "search")] - use crate::prelude::SearchableGraphOps; - use crate::{ - db::{ - api::view::{IndexSpec, IndexSpecBuilder}, - graph::{ - assertions::{search_edges, search_nodes}, - views::filter::model::{ - edge_filter::EdgeFilter, node_filter::NodeFilter, - property_filter::ops::PropertyFilterOps, ComposableFilter, - PropertyFilterFactory, TemporalPropertyFilterFactory, - }, - }, - }, - errors::GraphError, - prelude::{AdditionOps, Graph, IndexMutationOps, StableDecode}, - serialise::{GraphFolder, StableEncode}, - }; - use tempfile::{tempdir, TempDir}; - - fn temp_storage_path() -> std::path::PathBuf { - tempfile::tempdir().unwrap().path().to_path_buf() - } - - fn init_graph() -> Graph { - let graph = Graph::new(); - - let nodes = vec![ - ( - 1, - "pometry", - [("p1", 5u64), ("p2", 50u64)], - Some("fire_nation"), - [("x", true)], - ), - ( - 1, - "raphtory", - [("p1", 10u64), ("p2", 100u64)], - Some("water_tribe"), - [("y", false)], - ), - ]; - - for (time, name, props, group, metadata) in nodes { - let node = graph.add_node(time, name, props, group, None).unwrap(); - node.add_metadata(metadata).unwrap(); - } - - let edges = vec![ - ( - 1, - "pometry", - "raphtory", - [("e_p1", 3.2f64), ("e_p2", 10f64)], - None, - [("e_x", true)], - ), - ( - 1, - "raphtory", - "pometry", - [("e_p1", 4.0f64), ("e_p2", 20f64)], - None, - [("e_y", false)], - ), - ]; - - for (time, src, dst, props, label, metadata) in edges { - let edge = graph.add_edge(time, src, dst, props, label).unwrap(); - edge.add_metadata(metadata, label).unwrap(); - } - - graph - } - - #[test] - #[ignore = "TODO: #2372"] - fn test_with_all_props_index_spec() { - let graph = init_graph(); - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_all_node_properties_and_metadata() - .with_all_edge_properties_and_metadata() - .build(); - assert_eq!( - index_spec.props(&graph).to_vec(), - vec![ - vec!["x", "y"], - vec!["p1", "p2"], - vec!["e_x", "e_y"], - vec!["e_p1", "e_p2"] - ] - ); - graph.create_index_in_ram_with_spec(index_spec).unwrap(); - - let filter = NodeFilter - .property("p1") - .eq(5u64) - .and(NodeFilter.metadata("x").eq(true)); - let results = search_nodes(&graph, filter); - assert_eq!(results, vec!["pometry"]); - - let filter = EdgeFilter - .property("e_p1") - .lt(5f64) - .and(EdgeFilter.metadata("e_y").eq(false)); - let results = search_edges(&graph, filter); - assert_eq!(results, vec!["raphtory->pometry"]); - } - - #[test] - #[ignore = "TODO: #2372"] - fn test_with_selected_props_index_spec() { - let graph = init_graph(); - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_node_metadata(vec!["y"]) - .unwrap() - .with_node_properties(vec!["p1"]) - .unwrap() - .with_edge_metadata(vec!["e_y"]) - .unwrap() - .with_edge_properties(vec!["e_p1"]) - .unwrap() - .build(); - assert_eq!( - index_spec.props(&graph).to_vec(), - vec![vec!["y"], vec!["p1"], vec!["e_y"], vec!["e_p1"]] - ); - graph.create_index_in_ram_with_spec(index_spec).unwrap(); - - let filter = NodeFilter - .property("p1") - .eq(5u64) - .or(NodeFilter.metadata("y").eq(false)); - let results = search_nodes(&graph, filter); - assert_eq!(results, vec!["pometry", "raphtory"]); - - let filter = NodeFilter.metadata("y").eq(false); - let results = search_nodes(&graph, filter); - assert_eq!(results, vec!["raphtory"]); - - let filter = EdgeFilter - .property("e_p1") - .lt(5f64) - .or(EdgeFilter.metadata("e_y").eq(false)); - let results = search_edges(&graph, filter); - assert_eq!(results, vec!["pometry->raphtory", "raphtory->pometry"]); - } - - #[test] - fn test_with_invalid_property_returns_error() { - let graph = init_graph(); - let result = IndexSpecBuilder::new(graph.clone()).with_node_metadata(["xyz"]); - - assert!(matches!(result, Err(GraphError::PropertyMissingError(p)) if p == "xyz")); - } - - #[test] - fn test_build_empty_spec_by_default() { - let graph = init_graph(); - let index_spec = IndexSpecBuilder::new(graph.clone()).build(); - - assert!(index_spec.node_metadata.is_empty()); - assert!(index_spec.node_properties.is_empty()); - assert!(index_spec.edge_metadata.is_empty()); - assert!(index_spec.edge_properties.is_empty()); - - graph.create_index_in_ram_with_spec(index_spec).unwrap(); - - let filter = NodeFilter - .property("p1") - .eq(5u64) - .and(NodeFilter.metadata("x").eq(true)); - let results = search_nodes(&graph, filter); - assert_eq!(results, vec!["pometry"]); - - let filter = EdgeFilter - .property("e_p1") - .lt(5f64) - .or(EdgeFilter.metadata("e_y").eq(false)); - let results = search_edges(&graph, filter); - assert_eq!(results, vec!["pometry->raphtory", "raphtory->pometry"]); - } - - #[test] - #[ignore = "TODO: #2372"] - fn test_mixed_node_and_edge_props_index_spec() { - let graph = init_graph(); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_node_metadata(vec!["x"]) - .unwrap() - .with_all_node_properties() - .with_all_edge_properties_and_metadata() - .build(); - assert_eq!( - index_spec.props(&graph).to_vec(), - vec![ - vec!["x"], - vec!["p1", "p2"], - vec!["e_x", "e_y"], - vec!["e_p1", "e_p2"] - ] - ); - - graph.create_index_in_ram_with_spec(index_spec).unwrap(); - - let filter = NodeFilter - .property("p1") - .eq(5u64) - .or(NodeFilter.metadata("y").eq(false)); - let results = search_nodes(&graph, filter); - assert_eq!(results, vec!["pometry", "raphtory"]); - - let filter = EdgeFilter - .property("e_p1") - .lt(5f64) - .or(EdgeFilter.metadata("e_y").eq(false)); - let results = search_edges(&graph, filter); - assert_eq!(results, vec!["pometry->raphtory", "raphtory->pometry"]); - } - - #[test] - fn test_get_index_spec_newly_created_index() { - let graph = init_graph(); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_node_metadata(vec!["x"]) - .unwrap() - .with_all_node_properties() - .with_all_edge_properties_and_metadata() - .build(); - - graph - .create_index_in_ram_with_spec(index_spec.clone()) - .unwrap(); - - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - } - - #[test] - #[ignore = "TODO: #2372"] - fn test_get_index_spec_updated_index() { - let graph = init_graph(); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_edge_metadata(vec!["e_y"]) - .unwrap() - .build(); - graph.create_index_with_spec(index_spec.clone()).unwrap(); - - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - let results = search_nodes(&graph, NodeFilter.metadata("y").eq(false)); - assert_eq!(results, vec!["raphtory"]); - let results = search_edges(&graph, EdgeFilter.metadata("e_y").eq(false)); - assert_eq!(results, vec!["raphtory->pometry"]); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_node_metadata(vec!["y"]) - .unwrap() - .with_node_properties(vec!["p2"]) - .unwrap() - .with_edge_metadata(vec!["e_y"]) - .unwrap() - .build(); - graph.create_index_with_spec(index_spec.clone()).unwrap(); - - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - let results = search_nodes(&graph, NodeFilter.metadata("y").eq(false)); - assert_eq!(results, vec!["raphtory"]); - let results = search_edges(&graph, EdgeFilter.metadata("e_y").eq(false)); - assert_eq!(results, vec!["raphtory->pometry"]); - } - - #[test] - #[ignore = "TODO: #2372"] - fn test_get_index_spec_updated_index_persisted_and_loaded() { - let graph = init_graph(); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_edge_metadata(vec!["e_y"]) - .unwrap() - .build(); - graph.create_index_with_spec(index_spec.clone()).unwrap(); - - let tmp_graph_dir = tempdir().unwrap(); - let path = tmp_graph_dir.path().to_path_buf(); - graph.encode(&path).unwrap(); - let graph = Graph::decode(&path).unwrap(); - - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - let results = search_nodes(&graph, NodeFilter.metadata("y").eq(false)); - assert_eq!(results, vec!["raphtory"]); - let results = search_edges(&graph, EdgeFilter.metadata("e_y").eq(false)); - assert_eq!(results, vec!["raphtory->pometry"]); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_node_metadata(vec!["y"]) - .unwrap() - .with_node_properties(vec!["p2"]) - .unwrap() - .with_edge_metadata(vec!["e_y"]) - .unwrap() - .build(); - graph.create_index_with_spec(index_spec.clone()).unwrap(); - let tmp_graph_dir = tempdir().unwrap(); - let path = tmp_graph_dir.path().to_path_buf(); - graph.encode(path.clone()).unwrap(); - let graph = Graph::decode(&path).unwrap(); - - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - let results = search_nodes(&graph, NodeFilter.metadata("y").eq(false)); - assert_eq!(results, vec!["raphtory"]); - let results = search_edges(&graph, EdgeFilter.metadata("e_y").eq(false)); - assert_eq!(results, vec!["raphtory->pometry"]); - } - - #[test] - fn test_get_index_spec_loaded_index() { - let graph = init_graph(); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_node_metadata(vec!["y"]) - .unwrap() - .with_node_properties(vec!["p2"]) - .unwrap() - .with_edge_metadata(vec!["e_y"]) - .unwrap() - .with_edge_properties(vec!["e_p2"]) - .unwrap() - .build(); - - graph.create_index_with_spec(index_spec.clone()).unwrap(); - let tmp_graph_dir = tempdir().unwrap(); - let path = tmp_graph_dir.path().to_path_buf(); - graph.encode(path.clone()).unwrap(); - - let graph = Graph::decode(&path).unwrap(); - let index_spec2 = graph.get_index_spec().unwrap(); - - assert_eq!(index_spec, index_spec2); - } - - #[test] - fn test_get_index_spec_loaded_index_zip() { - let graph = init_graph(); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_node_metadata(vec!["y"]) - .unwrap() - .with_node_properties(vec!["p2"]) - .unwrap() - .with_edge_metadata(vec!["e_y"]) - .unwrap() - .build(); - graph.create_index_with_spec(index_spec.clone()).unwrap(); - - let binding = TempDir::new().unwrap(); - let path = binding.path(); - let folder = GraphFolder::new_as_zip(path); - graph.encode(folder).unwrap(); - - let graph = Graph::decode(path).unwrap(); - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - } - - #[test] - fn test_no_new_node_prop_index_created_via_update_apis() { - run_node_index_test(|graph, index_spec| { - graph.create_index_with_spec(index_spec.clone()) - }); - - run_node_index_test(|graph, index_spec| { - graph.create_index_in_ram_with_spec(index_spec.clone()) - }); - } - - #[test] - fn test_no_new_edge_prop_index_created_via_update_apis() { - run_edge_index_test(|graph, index_spec| { - graph.create_index_with_spec(index_spec.clone()) - }); - - run_edge_index_test(|graph, index_spec| { - graph.create_index_in_ram_with_spec(index_spec.clone()) - }); - } - - fn run_node_index_test(create_index_fn: F) - where - F: Fn(&Graph, IndexSpec) -> Result<(), GraphError>, - { - let graph = init_graph(); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_node_metadata(vec!["y"]) - .unwrap() - .with_node_properties(vec!["p1"]) - .unwrap() - .build(); - create_index_fn(&graph, index_spec.clone()).unwrap(); - - let filter = NodeFilter.property("p2").temporal().last().eq(50u64); - assert_eq!(search_nodes(&graph, filter.clone()), vec!["pometry"]); - - let node = graph - .add_node(1, "shivam", [("p1", 100u64)], Some("fire_nation"), None) - .unwrap(); - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - - let filter = NodeFilter.property("p1").temporal().last().eq(100u64); - assert_eq!(search_nodes(&graph, filter.clone()), vec!["shivam"]); - - node.add_metadata([("z", true)]).unwrap(); - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - let filter = NodeFilter.metadata("z").eq(true); - assert_eq!(search_nodes(&graph, filter.clone()), vec!["shivam"]); - - node.update_metadata([("z", false)]).unwrap(); - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - let filter = NodeFilter.metadata("z").eq(false); - assert_eq!(search_nodes(&graph, filter.clone()), vec!["shivam"]); - } - - fn run_edge_index_test(create_index_fn: F) - where - F: Fn(&Graph, IndexSpec) -> Result<(), GraphError>, - { - let graph = init_graph(); - - let index_spec = IndexSpecBuilder::new(graph.clone()) - .with_node_metadata(vec!["y"]) - .unwrap() - .with_node_properties(vec!["p2"]) - .unwrap() - .build(); - create_index_fn(&graph, index_spec.clone()).unwrap(); - - let edge = graph - .add_edge(1, "shivam", "kapoor", [("p1", 100u64)], None) - .unwrap(); - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - let filter = EdgeFilter.property("p1").temporal().last().eq(100u64); - assert_eq!(search_edges(&graph, filter.clone()), vec!["shivam->kapoor"]); - - edge.add_metadata([("z", true)], None).unwrap(); - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - let filter = EdgeFilter.metadata("z").eq(true); - assert_eq!(search_edges(&graph, filter.clone()), vec!["shivam->kapoor"]); - - edge.update_metadata([("z", false)], None).unwrap(); - assert_eq!(index_spec, graph.get_index_spec().unwrap()); - let filter = EdgeFilter.metadata("z").eq(false); - assert_eq!(search_edges(&graph, filter.clone()), vec!["shivam->kapoor"]); - } - } -} +mod test_index {} From 9d21e764e13dabd5d957e474f87bfc1541e3e5d4 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Wed, 20 May 2026 08:27:09 +0100 Subject: [PATCH 6/8] fmt --- raphtory-test-utils/tests/graph_index.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/raphtory-test-utils/tests/graph_index.rs b/raphtory-test-utils/tests/graph_index.rs index 915cc688d4..84b30deab2 100644 --- a/raphtory-test-utils/tests/graph_index.rs +++ b/raphtory-test-utils/tests/graph_index.rs @@ -525,7 +525,6 @@ mod test_index { } mod test_index_spec { - use raphtory::prelude::SearchableGraphOps; use raphtory::{ db::{ api::view::{IndexSpec, IndexSpecBuilder}, @@ -536,7 +535,7 @@ mod test_index { }, }, errors::GraphError, - prelude::{AdditionOps, Graph, IndexMutationOps, StableDecode}, + prelude::{AdditionOps, Graph, IndexMutationOps, SearchableGraphOps, StableDecode}, serialise::{GraphFolder, StableEncode}, }; use raphtory_test_utils::assertions::{search_edges, search_nodes}; From d073ddca829e9f7ccd4fdd2810c2cf1eed9fc451 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Wed, 20 May 2026 09:42:11 +0100 Subject: [PATCH 7/8] fix testutils in graphql --- raphtory-graphql/src/lib.rs | 20 +++++++++++++++++++- raphtory-test-utils/src/test_utils.rs | 19 ------------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/raphtory-graphql/src/lib.rs b/raphtory-graphql/src/lib.rs index 5a4c7b0cf6..2206573740 100644 --- a/raphtory-graphql/src/lib.rs +++ b/raphtory-graphql/src/lib.rs @@ -65,7 +65,6 @@ mod graphql_test { }, prelude::*, serialise::GraphFolder, - test_utils::json_sort_by_name, }; use raphtory_api::core::{entities::GID, storage::arc_str::ArcStr}; use serde_json::{json, Value}; @@ -1927,4 +1926,23 @@ mod graphql_test { "node types returned by GraphQL should match those set on ingest" ); } + + pub fn json_sort_by_name(value: Value) -> Value { + match value { + Value::Array(inner) => Value::Array( + inner + .into_iter() + .sorted_by(|l, r| name_sort_key(l).cmp(&name_sort_key(r))) + .map(|inner_value| json_sort_by_name(inner_value)) + .collect(), + ), + Value::Object(inner) => Value::Object( + inner + .into_iter() + .map(|(key, value)| (key, json_sort_by_name(value))) + .collect(), + ), + value => value, + } + } } diff --git a/raphtory-test-utils/src/test_utils.rs b/raphtory-test-utils/src/test_utils.rs index bc5ea41cea..e602a81304 100644 --- a/raphtory-test-utils/src/test_utils.rs +++ b/raphtory-test-utils/src/test_utils.rs @@ -64,25 +64,6 @@ fn name_sort_key(value: &Value) -> Option> { } } -pub fn json_sort_by_name(value: Value) -> Value { - match value { - Value::Array(inner) => Value::Array( - inner - .into_iter() - .sorted_by(|l, r| name_sort_key(l).cmp(&name_sort_key(r))) - .map(|inner_value| json_sort_by_name(inner_value)) - .collect(), - ), - Value::Object(inner) => Value::Object( - inner - .into_iter() - .map(|(key, value)| (key, json_sort_by_name(value))) - .collect(), - ), - value => value, - } -} - pub fn test_graph(graph: &Graph, test: impl FnOnce(&Graph)) { test(graph) } From 72f9def8aa7578af293960e95d8e6c6c83afb238 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Wed, 20 May 2026 09:46:07 +0100 Subject: [PATCH 8/8] fix graphql testutils --- raphtory-graphql/src/lib.rs | 26 ++++++++++++++++++++++++++ raphtory-test-utils/src/test_utils.rs | 26 -------------------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/raphtory-graphql/src/lib.rs b/raphtory-graphql/src/lib.rs index 2206573740..2f98e89f49 100644 --- a/raphtory-graphql/src/lib.rs +++ b/raphtory-graphql/src/lib.rs @@ -1927,6 +1927,12 @@ mod graphql_test { ); } + #[derive(PartialEq, Eq, PartialOrd, Ord)] + pub enum NameSortKey<'a> { + Node(&'a str), + Edge(&'a str, &'a str), + } + pub fn json_sort_by_name(value: Value) -> Value { match value { Value::Array(inner) => Value::Array( @@ -1945,4 +1951,24 @@ mod graphql_test { value => value, } } + + fn name_sort_key(value: &Value) -> Option> { + match value { + Value::Object(inner) => inner + .get("name") + .and_then(|name| Some(NameSortKey::Node(name.as_str()?))) + .or_else(|| { + inner.get("id").and_then(|id| match id { + Value::String(node) => Some(NameSortKey::Node(node)), + Value::Array(edge) => { + let (src, dst) = + edge.iter().map(|e| e.as_str().unwrap()).next_tuple()?; + Some(NameSortKey::Edge(src, dst)) + } + _ => None, + }) + }), + _ => None, + } + } } diff --git a/raphtory-test-utils/src/test_utils.rs b/raphtory-test-utils/src/test_utils.rs index e602a81304..0790745460 100644 --- a/raphtory-test-utils/src/test_utils.rs +++ b/raphtory-test-utils/src/test_utils.rs @@ -29,7 +29,6 @@ use serde::{ ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer, }; -use serde_json::Value; use std::{ borrow::Cow, collections::{hash_map, HashMap}, @@ -39,31 +38,6 @@ use std::{ sync::Arc, }; -#[derive(PartialEq, Eq, PartialOrd, Ord)] -pub enum NameSortKey<'a> { - Node(&'a str), - Edge(&'a str, &'a str), -} - -fn name_sort_key(value: &Value) -> Option> { - match value { - Value::Object(inner) => inner - .get("name") - .and_then(|name| Some(NameSortKey::Node(name.as_str()?))) - .or_else(|| { - inner.get("id").and_then(|id| match id { - Value::String(node) => Some(NameSortKey::Node(node)), - Value::Array(edge) => { - let (src, dst) = edge.iter().map(|e| e.as_str().unwrap()).next_tuple()?; - Some(NameSortKey::Edge(src, dst)) - } - _ => None, - }) - }), - _ => None, - } -} - pub fn test_graph(graph: &Graph, test: impl FnOnce(&Graph)) { test(graph) }