diff --git a/docs/reference/graphql/graphql_API.md b/docs/reference/graphql/graphql_API.md index 82ff44d718..3f85bd9b5c 100644 --- a/docs/reference/graphql/graphql_API.md +++ b/docs/reference/graphql/graphql_API.md @@ -3367,6 +3367,11 @@ Ordered list of view operations; each entry is a one-of variant applied to the r +weight +String + + + shortest_path [ShortestPathOutput!]! @@ -8279,6 +8284,30 @@ Optional `{start, end}` to restrict matches to edges active in that interval. ## Inputs +### DegreeFilterNew + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
directionDegreeDirection!
wherePropCondition!
+ ### EdgeAddition @@ -9919,6 +9948,15 @@ Filters a built-in node field (ID, name, or type). Filters a node property by name and condition. + + + + + + @@ -11580,6 +11618,45 @@ All properties.
degreeDegreeFilterNew + +Filters a node's degree (in, out, or total) by a condition. +
+### DegreeDirection + +Filters nodes by computed degree with a directional scope. + +`DegreeFilterNew` lets callers filter on: +- inbound degree (`IN`), +- outbound degree (`OUT`), +- or total degree (`BOTH`). + +The selected degree is compared using the `where` condition. + +Example (GraphQL): +```graphql +{ Degree: { direction: BOTH, where: { Gt: 10 } } } +``` + + + + + + + + + + + + + + + + + + + + + + +
ValueDescription
IN
OUT
BOTH
+ ### GraphType diff --git a/examples/rust/src/bin/crypto/main.rs b/examples/rust/src/bin/crypto/main.rs index 5ee2f54836..1579eed22d 100644 --- a/examples/rust/src/bin/crypto/main.rs +++ b/examples/rust/src/bin/crypto/main.rs @@ -1,8 +1,7 @@ use itertools::Itertools; use raphtory::{ algorithms::{ - centrality::pagerank::page_rank, - pathing::temporal_reachability::temporally_reachable_nodes, + centrality::pagerank::page_rank, pathing::temporal_reachability::temporally_reachable_nodes, }, db::api::view::*, graph_loader::stable_coins::stable_coin_graph, diff --git a/examples/rust/src/bin/pokec/main.rs b/examples/rust/src/bin/pokec/main.rs index 776c7f685a..56bda2b968 100644 --- a/examples/rust/src/bin/pokec/main.rs +++ b/examples/rust/src/bin/pokec/main.rs @@ -1,7 +1,5 @@ use raphtory::{ - algorithms::{ - centrality::pagerank::page_rank, components::weakly_connected_components, - }, + algorithms::{centrality::pagerank::page_rank, components::weakly_connected_components}, db::{api::mutation::AdditionOps, graph::graph::Graph}, io::csv_loader::CsvLoader, logging::global_info_logger, diff --git a/python/python/raphtory/filter/__init__.pyi b/python/python/raphtory/filter/__init__.pyi index 05eb32788e..af99dbcee9 100644 --- a/python/python/raphtory/filter/__init__.pyi +++ b/python/python/raphtory/filter/__init__.pyi @@ -341,6 +341,15 @@ class Node(object): filter.FilterExpr: """ + @staticmethod + def degree(): + """ + Selects total node degree for filtering. + + Returns: + filter.FilterOps + """ + @staticmethod def id() -> filter.NodeIdFilterBuilder: """ @@ -350,6 +359,15 @@ class Node(object): filter.NodeIdFilterBuilder: """ + @staticmethod + def in_degree(): + """ + Selects incoming node degree for filtering. + + Returns: + filter.FilterOps + """ + @staticmethod def is_active() -> filter.FilterExpr: """ @@ -424,6 +442,15 @@ class Node(object): filter.NodeTypeFilterBuilder: """ + @staticmethod + def out_degree(): + """ + Selects outgoing node degree for filtering. + + Returns: + filter.FilterOps + """ + @staticmethod def property(name: str) -> filter.PropertyFilterOps: """ diff --git a/python/tests/test_base_install/test_filters/test_node_filter.py b/python/tests/test_base_install/test_filters/test_node_filter.py index 18084b1261..fb32637fe4 100644 --- a/python/tests/test_base_install/test_filters/test_node_filter.py +++ b/python/tests/test_base_install/test_filters/test_node_filter.py @@ -36,62 +36,170 @@ def metric_value(node): ) filtered_event_nodes = sort_vids(graph.filter(filter_expr).nodes.id) - assert filtered_event_nodes == expected_filter_nodes, ( - f"{context} failed for event graph" - ) + assert ( + filtered_event_nodes == expected_filter_nodes + ), f"{context} failed for event graph" selected_event_nodes = sort_vids(graph.nodes[filter_expr].id) - assert selected_event_nodes == expected_select_nodes, ( - f"{context} failed for event graph select" - ) + assert ( + selected_event_nodes == expected_select_nodes + ), f"{context} failed for event graph select" persistent_graph = graph.persistent_graph() filtered_persistent_nodes = sort_vids(persistent_graph.filter(filter_expr).nodes.id) - assert filtered_persistent_nodes == expected_filter_nodes, ( - f"{context} failed for persistent graph" - ) + assert ( + filtered_persistent_nodes == expected_filter_nodes + ), f"{context} failed for persistent graph" selected_persistent_nodes = sort_vids(persistent_graph.nodes[filter_expr].id) - assert selected_persistent_nodes == expected_select_nodes, ( - f"{context} failed for persistent graph select" - ) + assert ( + selected_persistent_nodes == expected_select_nodes + ), f"{context} failed for persistent graph select" @pytest.mark.parametrize("value", range(0, 15)) def test_degree_filter_both_direction_comparison(value): graph = degree_graph_with_add_node_and_add_edge(Graph()) - assert_filter(graph, filter.Node.degree() < value, "both", lambda d: d < value, f"BOTH < {value}") - assert_filter(graph, filter.Node.degree() <= value, "both", lambda d: d <= value, f"BOTH <= {value}") - assert_filter(graph, filter.Node.degree() == value, "both", lambda d: d == value, f"BOTH == {value}") - assert_filter(graph, filter.Node.degree() != value, "both", lambda d: d != value, f"BOTH != {value}") - assert_filter(graph, filter.Node.degree() >= value, "both", lambda d: d >= value, f"BOTH >= {value}") - assert_filter(graph, filter.Node.degree() > value, "both", lambda d: d > value, f"BOTH > {value}") + assert_filter( + graph, + filter.Node.degree() < value, + "both", + lambda d: d < value, + f"BOTH < {value}", + ) + assert_filter( + graph, + filter.Node.degree() <= value, + "both", + lambda d: d <= value, + f"BOTH <= {value}", + ) + assert_filter( + graph, + filter.Node.degree() == value, + "both", + lambda d: d == value, + f"BOTH == {value}", + ) + assert_filter( + graph, + filter.Node.degree() != value, + "both", + lambda d: d != value, + f"BOTH != {value}", + ) + assert_filter( + graph, + filter.Node.degree() >= value, + "both", + lambda d: d >= value, + f"BOTH >= {value}", + ) + assert_filter( + graph, + filter.Node.degree() > value, + "both", + lambda d: d > value, + f"BOTH > {value}", + ) @pytest.mark.parametrize("value", range(0, 15)) def test_degree_filter_in_direction_comparison(value): graph = degree_graph_with_add_node_and_add_edge(Graph()) - assert_filter(graph, filter.Node.in_degree() < value, "in", lambda d: d < value, f"IN < {value}") - assert_filter(graph, filter.Node.in_degree() <= value, "in", lambda d: d <= value, f"IN <= {value}") - assert_filter(graph, filter.Node.in_degree() == value, "in", lambda d: d == value, f"IN == {value}") - assert_filter(graph, filter.Node.in_degree() != value, "in", lambda d: d != value, f"IN != {value}") - assert_filter(graph, filter.Node.in_degree() >= value, "in", lambda d: d >= value, f"IN >= {value}") - assert_filter(graph, filter.Node.in_degree() > value, "in", lambda d: d > value, f"IN > {value}") + assert_filter( + graph, + filter.Node.in_degree() < value, + "in", + lambda d: d < value, + f"IN < {value}", + ) + assert_filter( + graph, + filter.Node.in_degree() <= value, + "in", + lambda d: d <= value, + f"IN <= {value}", + ) + assert_filter( + graph, + filter.Node.in_degree() == value, + "in", + lambda d: d == value, + f"IN == {value}", + ) + assert_filter( + graph, + filter.Node.in_degree() != value, + "in", + lambda d: d != value, + f"IN != {value}", + ) + assert_filter( + graph, + filter.Node.in_degree() >= value, + "in", + lambda d: d >= value, + f"IN >= {value}", + ) + assert_filter( + graph, + filter.Node.in_degree() > value, + "in", + lambda d: d > value, + f"IN > {value}", + ) @pytest.mark.parametrize("value", range(0, 15)) def test_degree_filter_out_direction_comparison(value): graph = degree_graph_with_add_node_and_add_edge(Graph()) - assert_filter(graph, filter.Node.out_degree() < value, "out", lambda d: d < value, f"OUT < {value}") - assert_filter(graph, filter.Node.out_degree() <= value, "out", lambda d: d <= value, f"OUT <= {value}") - assert_filter(graph, filter.Node.out_degree() == value, "out", lambda d: d == value, f"OUT == {value}") - assert_filter(graph, filter.Node.out_degree() != value, "out", lambda d: d != value, f"OUT != {value}") - assert_filter(graph, filter.Node.out_degree() >= value, "out", lambda d: d >= value, f"OUT >= {value}") - assert_filter(graph, filter.Node.out_degree() > value, "out", lambda d: d > value, f"OUT > {value}") + assert_filter( + graph, + filter.Node.out_degree() < value, + "out", + lambda d: d < value, + f"OUT < {value}", + ) + assert_filter( + graph, + filter.Node.out_degree() <= value, + "out", + lambda d: d <= value, + f"OUT <= {value}", + ) + assert_filter( + graph, + filter.Node.out_degree() == value, + "out", + lambda d: d == value, + f"OUT == {value}", + ) + assert_filter( + graph, + filter.Node.out_degree() != value, + "out", + lambda d: d != value, + f"OUT != {value}", + ) + assert_filter( + graph, + filter.Node.out_degree() >= value, + "out", + lambda d: d >= value, + f"OUT >= {value}", + ) + assert_filter( + graph, + filter.Node.out_degree() > value, + "out", + lambda d: d > value, + f"OUT > {value}", + ) @pytest.mark.parametrize("value", range(0, 15)) @@ -330,26 +438,134 @@ def test_degree_filter_with_string_threshold(value): threshold_str = str(value) parsed_str = int(threshold_str) - assert_filter(graph, filter.Node.degree() < threshold_str, "both", lambda d: d < parsed_str, f"BOTH < string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.degree() <= threshold_str, "both", lambda d: d <= parsed_str, f"BOTH <= string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.degree() == threshold_str, "both", lambda d: d == parsed_str, f"BOTH == string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.degree() != threshold_str, "both", lambda d: d != parsed_str, f"BOTH != string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.degree() >= threshold_str, "both", lambda d: d >= parsed_str, f"BOTH >= string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.degree() > threshold_str, "both", lambda d: d > parsed_str, f"BOTH > string threshold parsed to u64 ({threshold_str})") + assert_filter( + graph, + filter.Node.degree() < threshold_str, + "both", + lambda d: d < parsed_str, + f"BOTH < string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.degree() <= threshold_str, + "both", + lambda d: d <= parsed_str, + f"BOTH <= string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.degree() == threshold_str, + "both", + lambda d: d == parsed_str, + f"BOTH == string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.degree() != threshold_str, + "both", + lambda d: d != parsed_str, + f"BOTH != string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.degree() >= threshold_str, + "both", + lambda d: d >= parsed_str, + f"BOTH >= string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.degree() > threshold_str, + "both", + lambda d: d > parsed_str, + f"BOTH > string threshold parsed to u64 ({threshold_str})", + ) - assert_filter(graph, filter.Node.in_degree() < threshold_str, "in", lambda d: d < parsed_str, f"IN < string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.in_degree() <= threshold_str, "in", lambda d: d <= parsed_str, f"IN <= string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.in_degree() == threshold_str, "in", lambda d: d == parsed_str, f"IN == string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.in_degree() != threshold_str, "in", lambda d: d != parsed_str, f"IN != string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.in_degree() >= threshold_str, "in", lambda d: d >= parsed_str, f"IN >= string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.in_degree() > threshold_str, "in", lambda d: d > parsed_str, f"IN > string threshold parsed to u64 ({threshold_str})") + assert_filter( + graph, + filter.Node.in_degree() < threshold_str, + "in", + lambda d: d < parsed_str, + f"IN < string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.in_degree() <= threshold_str, + "in", + lambda d: d <= parsed_str, + f"IN <= string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.in_degree() == threshold_str, + "in", + lambda d: d == parsed_str, + f"IN == string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.in_degree() != threshold_str, + "in", + lambda d: d != parsed_str, + f"IN != string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.in_degree() >= threshold_str, + "in", + lambda d: d >= parsed_str, + f"IN >= string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.in_degree() > threshold_str, + "in", + lambda d: d > parsed_str, + f"IN > string threshold parsed to u64 ({threshold_str})", + ) - assert_filter(graph, filter.Node.out_degree() < threshold_str, "out", lambda d: d < parsed_str, f"OUT < string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.out_degree() <= threshold_str, "out", lambda d: d <= parsed_str, f"OUT <= string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.out_degree() == threshold_str, "out", lambda d: d == parsed_str, f"OUT == string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.out_degree() != threshold_str, "out", lambda d: d != parsed_str, f"OUT != string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.out_degree() >= threshold_str, "out", lambda d: d >= parsed_str, f"OUT >= string threshold parsed to u64 ({threshold_str})") - assert_filter(graph, filter.Node.out_degree() > threshold_str, "out", lambda d: d > parsed_str, f"OUT > string threshold parsed to u64 ({threshold_str})") + assert_filter( + graph, + filter.Node.out_degree() < threshold_str, + "out", + lambda d: d < parsed_str, + f"OUT < string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.out_degree() <= threshold_str, + "out", + lambda d: d <= parsed_str, + f"OUT <= string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.out_degree() == threshold_str, + "out", + lambda d: d == parsed_str, + f"OUT == string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.out_degree() != threshold_str, + "out", + lambda d: d != parsed_str, + f"OUT != string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.out_degree() >= threshold_str, + "out", + lambda d: d >= parsed_str, + f"OUT >= string threshold parsed to u64 ({threshold_str})", + ) + assert_filter( + graph, + filter.Node.out_degree() > threshold_str, + "out", + lambda d: d > parsed_str, + f"OUT > string threshold parsed to u64 ({threshold_str})", + ) @pytest.mark.parametrize("value", range(0, 15)) @@ -359,9 +575,27 @@ def test_degree_filter_with_string_is_in(value): threshold_b_str = str(value + 1) set_values = [int(threshold_a_str), int(threshold_b_str)] - assert_filter(graph, filter.Node.degree().is_in([threshold_a_str, threshold_b_str]), "both", lambda d: d in set_values, f"BOTH is_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})") - assert_filter(graph, filter.Node.in_degree().is_in([threshold_a_str, threshold_b_str]), "in", lambda d: d in set_values, f"IN is_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})") - assert_filter(graph, filter.Node.out_degree().is_in([threshold_a_str, threshold_b_str]), "out", lambda d: d in set_values, f"OUT is_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})") + assert_filter( + graph, + filter.Node.degree().is_in([threshold_a_str, threshold_b_str]), + "both", + lambda d: d in set_values, + f"BOTH is_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})", + ) + assert_filter( + graph, + filter.Node.in_degree().is_in([threshold_a_str, threshold_b_str]), + "in", + lambda d: d in set_values, + f"IN is_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})", + ) + assert_filter( + graph, + filter.Node.out_degree().is_in([threshold_a_str, threshold_b_str]), + "out", + lambda d: d in set_values, + f"OUT is_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})", + ) @pytest.mark.parametrize("value", range(0, 15)) @@ -371,9 +605,27 @@ def test_degree_filter_with_string_is_not_in(value): threshold_b_str = str(value + 1) set_values = [int(threshold_a_str), int(threshold_b_str)] - assert_filter(graph, filter.Node.degree().is_not_in([threshold_a_str, threshold_b_str]), "both", lambda d: d not in set_values, f"BOTH is_not_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})") - assert_filter(graph, filter.Node.in_degree().is_not_in([threshold_a_str, threshold_b_str]), "in", lambda d: d not in set_values, f"IN is_not_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})") - assert_filter(graph, filter.Node.out_degree().is_not_in([threshold_a_str, threshold_b_str]), "out", lambda d: d not in set_values, f"OUT is_not_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})") + assert_filter( + graph, + filter.Node.degree().is_not_in([threshold_a_str, threshold_b_str]), + "both", + lambda d: d not in set_values, + f"BOTH is_not_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})", + ) + assert_filter( + graph, + filter.Node.in_degree().is_not_in([threshold_a_str, threshold_b_str]), + "in", + lambda d: d not in set_values, + f"IN is_not_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})", + ) + assert_filter( + graph, + filter.Node.out_degree().is_not_in([threshold_a_str, threshold_b_str]), + "out", + lambda d: d not in set_values, + f"OUT is_not_in(string thresholds parsed to u64) ({threshold_a_str}, {threshold_b_str})", + ) @pytest.mark.parametrize("value", range(0, 15)) @@ -572,8 +824,6 @@ def test_degree_filter_with_float_is_not_in(value): ) - - @with_variants(init_graph) def test_filter_nodes_for_node_name_eq(): def check(graph): @@ -1034,4 +1284,4 @@ def check(graph): with pytest.raises(AttributeError, match=r"has no attribute 'len'"): filter.Node.name().len() - return check \ No newline at end of file + return check diff --git a/python/tests/test_base_install/test_graphdb/test_algorithms.py b/python/tests/test_base_install/test_graphdb/test_algorithms.py index e076376b44..efb191659e 100644 --- a/python/tests/test_base_install/test_graphdb/test_algorithms.py +++ b/python/tests/test_base_install/test_graphdb/test_algorithms.py @@ -369,9 +369,9 @@ def test_weighted_page_rank(): ("3", 0.42311), ("4", 0.07837), ]: - assert abs(actual[node]["pagerank_score"] - expected) < 1e-5, ( - f"node {node}: {actual[node]} != {expected}" - ) + assert ( + abs(actual[node]["pagerank_score"] - expected) < 1e-5 + ), f"node {node}: {actual[node]} != {expected}" def test_weighted_page_rank_none_matches_unweighted(): diff --git a/python/tests/test_base_install/test_graphql/test_filters/test_node_filter_gql.py b/python/tests/test_base_install/test_graphql/test_filters/test_node_filter_gql.py index a81fbf240b..8fd9d8e9be 100644 --- a/python/tests/test_base_install/test_graphql/test_filters/test_node_filter_gql.py +++ b/python/tests/test_base_install/test_graphql/test_filters/test_node_filter_gql.py @@ -1,7 +1,16 @@ import pytest from raphtory import Graph, PersistentGraph -from filters_setup import init_graph, init_graph2, degree_graph_with_add_node_and_add_edge -from utils import run_graphql_test, run_graphql_error_test, run_graphql_error_test_contains, run_group_graphql_test +from filters_setup import ( + init_graph, + init_graph2, + degree_graph_with_add_node_and_add_edge, +) +from utils import ( + run_graphql_test, + run_graphql_error_test, + run_graphql_error_test_contains, + run_group_graphql_test, +) EVENT_GRAPH = init_graph(Graph()) PERSISTENT_GRAPH = init_graph(PersistentGraph()) @@ -169,27 +178,35 @@ def test_nodes_filter_windowed_is_not_active(graph): def _degree_value(node, direction): - if direction == "BOTH": - return node.degree() - if direction == "IN": - return node.in_degree() - if direction == "OUT": - return node.out_degree() - raise ValueError(f"Unsupported direction: {direction}") + if direction == "BOTH": + return node.degree() + if direction == "IN": + return node.in_degree() + if direction == "OUT": + return node.out_degree() + raise ValueError(f"Unsupported direction: {direction}") def _expected_degree_names(graph, direction, predicate): - candidate_ids = [node.id for node in graph.nodes if predicate(_degree_value(node, direction))] - subgraph = graph.subgraph(candidate_ids) - return sorted(str(node.id) for node in subgraph.nodes if len(node.history.collect()) > 0) + candidate_ids = [ + node.id for node in graph.nodes if predicate(_degree_value(node, direction)) + ] + subgraph = graph.subgraph(candidate_ids) + return sorted( + str(node.id) for node in subgraph.nodes if len(node.history.collect()) > 0 + ) def _expected_degree_select_names(graph, direction, predicate): - return sorted(str(node.id) for node in graph.nodes if predicate(_degree_value(node, direction))) + return sorted( + str(node.id) + for node in graph.nodes + if predicate(_degree_value(node, direction)) + ) def _degree_filter_nodes_query_expected_pair(expr, expected_names): - query = f""" + query = f""" query {{ graph(path: "g") {{ filterNodes(expr: {{ {expr} }}) {{ @@ -201,18 +218,18 @@ def _degree_filter_nodes_query_expected_pair(expr, expected_names): }} """ - expected_output = { - "graph": { - "filterNodes": { - "nodes": {"list": [{"name": name} for name in expected_names]} - } + expected_output = { + "graph": { + "filterNodes": { + "nodes": {"list": [{"name": name} for name in expected_names]} + } + } } - } - return query, expected_output + return query, expected_output def _degree_select_nodes_query_expected_pair(expr, expected_names): - query = f""" + query = f""" query {{ graph(path: "g") {{ nodes {{ @@ -224,199 +241,293 @@ def _degree_select_nodes_query_expected_pair(expr, expected_names): }} """ - expected_output = { - "graph": { - "nodes": { - "select": {"list": [{"name": name} for name in expected_names]} - } + expected_output = { + "graph": { + "nodes": {"select": {"list": [{"name": name} for name in expected_names]}} + } } - } - return query, expected_output + return query, expected_output @pytest.mark.parametrize("graph", [EVENT_GRAPH, PERSISTENT_GRAPH]) def test_filter_nodes_degree_ops_and_gql(graph): - threshold = 4 - queries_and_expected_outputs = [] - - for direction in ["BOTH", "IN", "OUT"]: - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d < threshold), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }}", - _expected_degree_names(graph, direction, lambda d: d < threshold), - )) - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ le: {{ u64: {threshold} }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d <= threshold), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ le: {{ u64: {threshold} }} }} }}", - _expected_degree_names(graph, direction, lambda d: d <= threshold), - )) - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ eq: {{ u64: {threshold} }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d == threshold), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ eq: {{ u64: {threshold} }} }} }}", - _expected_degree_names(graph, direction, lambda d: d == threshold), - )) - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ ne: {{ u64: {threshold} }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d != threshold), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ ne: {{ u64: {threshold} }} }} }}", - _expected_degree_names(graph, direction, lambda d: d != threshold), - )) - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ ge: {{ u64: {threshold} }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d >= threshold), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ ge: {{ u64: {threshold} }} }} }}", - _expected_degree_names(graph, direction, lambda d: d >= threshold), - )) - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ gt: {{ u64: {threshold} }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d > threshold), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ gt: {{ u64: {threshold} }} }} }}", - _expected_degree_names(graph, direction, lambda d: d > threshold), - )) - - run_group_graphql_test(queries_and_expected_outputs, graph, sort_output=True) + threshold = 4 + queries_and_expected_outputs = [] + + for direction in ["BOTH", "IN", "OUT"]: + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }}", + _expected_degree_select_names( + graph, direction, lambda d: d < threshold + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }}", + _expected_degree_names(graph, direction, lambda d: d < threshold), + ) + ) + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ le: {{ u64: {threshold} }} }} }}", + _expected_degree_select_names( + graph, direction, lambda d: d <= threshold + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ le: {{ u64: {threshold} }} }} }}", + _expected_degree_names(graph, direction, lambda d: d <= threshold), + ) + ) + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ eq: {{ u64: {threshold} }} }} }}", + _expected_degree_select_names( + graph, direction, lambda d: d == threshold + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ eq: {{ u64: {threshold} }} }} }}", + _expected_degree_names(graph, direction, lambda d: d == threshold), + ) + ) + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ ne: {{ u64: {threshold} }} }} }}", + _expected_degree_select_names( + graph, direction, lambda d: d != threshold + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ ne: {{ u64: {threshold} }} }} }}", + _expected_degree_names(graph, direction, lambda d: d != threshold), + ) + ) + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ ge: {{ u64: {threshold} }} }} }}", + _expected_degree_select_names( + graph, direction, lambda d: d >= threshold + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ ge: {{ u64: {threshold} }} }} }}", + _expected_degree_names(graph, direction, lambda d: d >= threshold), + ) + ) + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ gt: {{ u64: {threshold} }} }} }}", + _expected_degree_select_names( + graph, direction, lambda d: d > threshold + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ gt: {{ u64: {threshold} }} }} }}", + _expected_degree_names(graph, direction, lambda d: d > threshold), + ) + ) + + run_group_graphql_test(queries_and_expected_outputs, graph, sort_output=True) @pytest.mark.parametrize("graph", [EVENT_GRAPH, PERSISTENT_GRAPH]) def test_filter_nodes_degree_logic_and_sets_gql(graph): - threshold = 3 - upper = threshold + 5 - queries_and_expected_outputs = [] - - for direction in ["BOTH", "IN", "OUT"]: - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - "and: [" - f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {threshold} }} }} }} }}," - f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {upper} }} }} }} }}" - "]", - _expected_degree_select_names(graph, direction, lambda d: d > threshold and d < upper), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - "and: [" - f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {threshold} }} }} }} }}," - f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {upper} }} }} }} }}" - "]", - _expected_degree_names(graph, direction, lambda d: d > threshold and d < upper), - )) - - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - "or: [" - f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }} }}," - f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {upper} }} }} }} }}" - "]", - _expected_degree_select_names(graph, direction, lambda d: d < threshold or d > upper), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - "or: [" - f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }} }}," - f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {upper} }} }} }} }}" - "]", - _expected_degree_names(graph, direction, lambda d: d < threshold or d > upper), - )) - - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - "or: [" - f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }} }}," - "{ not: " - f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {upper} }} }} }} }}" - " }" - "]", - _expected_degree_select_names(graph, direction, lambda d: d < threshold or d <= upper), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - "or: [" - f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }} }}," - "{ not: " - f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {upper} }} }} }} }}" - " }" - "]", - _expected_degree_names(graph, direction, lambda d: d < threshold or d <= upper), - )) - - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ isIn: {{ list: [{{u64: {threshold}}}, {{u64: {threshold + 1}}}] }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d in [threshold, threshold + 1]), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ isIn: {{ list: [{{u64: {threshold}}}, {{u64: {threshold + 1}}}] }} }} }}", - _expected_degree_names(graph, direction, lambda d: d in [threshold, threshold + 1]), - )) - - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ isNotIn: {{ list: [{{u64: {threshold}}}, {{u64: {threshold + 1}}}] }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d not in [threshold, threshold + 1]), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ isNotIn: {{ list: [{{u64: {threshold}}}, {{u64: {threshold + 1}}}] }} }} }}", - _expected_degree_names(graph, direction, lambda d: d not in [threshold, threshold + 1]), - )) - - run_group_graphql_test(queries_and_expected_outputs, graph, sort_output=True) + threshold = 3 + upper = threshold + 5 + queries_and_expected_outputs = [] + + for direction in ["BOTH", "IN", "OUT"]: + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + "and: [" + f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {threshold} }} }} }} }}," + f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {upper} }} }} }} }}" + "]", + _expected_degree_select_names( + graph, direction, lambda d: d > threshold and d < upper + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + "and: [" + f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {threshold} }} }} }} }}," + f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {upper} }} }} }} }}" + "]", + _expected_degree_names( + graph, direction, lambda d: d > threshold and d < upper + ), + ) + ) + + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + "or: [" + f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }} }}," + f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {upper} }} }} }} }}" + "]", + _expected_degree_select_names( + graph, direction, lambda d: d < threshold or d > upper + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + "or: [" + f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }} }}," + f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {upper} }} }} }} }}" + "]", + _expected_degree_names( + graph, direction, lambda d: d < threshold or d > upper + ), + ) + ) + + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + "or: [" + f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }} }}," + "{ not: " + f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {upper} }} }} }} }}" + " }" + "]", + _expected_degree_select_names( + graph, direction, lambda d: d < threshold or d <= upper + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + "or: [" + f"{{ degree: {{ direction: {direction}, where: {{ lt: {{ u64: {threshold} }} }} }} }}," + "{ not: " + f"{{ degree: {{ direction: {direction}, where: {{ gt: {{ u64: {upper} }} }} }} }}" + " }" + "]", + _expected_degree_names( + graph, direction, lambda d: d < threshold or d <= upper + ), + ) + ) + + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ isIn: {{ list: [{{u64: {threshold}}}, {{u64: {threshold + 1}}}] }} }} }}", + _expected_degree_select_names( + graph, direction, lambda d: d in [threshold, threshold + 1] + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ isIn: {{ list: [{{u64: {threshold}}}, {{u64: {threshold + 1}}}] }} }} }}", + _expected_degree_names( + graph, direction, lambda d: d in [threshold, threshold + 1] + ), + ) + ) + + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ isNotIn: {{ list: [{{u64: {threshold}}}, {{u64: {threshold + 1}}}] }} }} }}", + _expected_degree_select_names( + graph, direction, lambda d: d not in [threshold, threshold + 1] + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ isNotIn: {{ list: [{{u64: {threshold}}}, {{u64: {threshold + 1}}}] }} }} }}", + _expected_degree_names( + graph, direction, lambda d: d not in [threshold, threshold + 1] + ), + ) + ) + + run_group_graphql_test(queries_and_expected_outputs, graph, sort_output=True) @pytest.mark.parametrize("graph", [EVENT_GRAPH, PERSISTENT_GRAPH]) def test_filter_nodes_degree_numeric_coercion_gql(graph): - threshold_str = "4" - threshold_float = 4.5 - queries_and_expected_outputs = [] - - for direction in ["BOTH", "IN", "OUT"]: - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f'degree: {{ direction: {direction}, where: {{ eq: {{ str: "{threshold_str}" }} }} }}', - _expected_degree_select_names(graph, direction, lambda d: d == int(threshold_str)), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f'degree: {{ direction: {direction}, where: {{ eq: {{ str: "{threshold_str}" }} }} }}', - _expected_degree_names(graph, direction, lambda d: d == int(threshold_str)), - )) - - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ ge: {{ f64: {threshold_float} }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d >= int(threshold_float)), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ ge: {{ f64: {threshold_float} }} }} }}", - _expected_degree_names(graph, direction, lambda d: d >= int(threshold_float)), - )) - - queries_and_expected_outputs.append(_degree_select_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ isIn: {{ list: [{{str: \"3\"}}, {{f64: 4.9}}] }} }} }}", - _expected_degree_select_names(graph, direction, lambda d: d in [3, 4]), - )) - queries_and_expected_outputs.append(_degree_filter_nodes_query_expected_pair( - f"degree: {{ direction: {direction}, where: {{ isIn: {{ list: [{{str: \"3\"}}, {{f64: 4.9}}] }} }} }}", - _expected_degree_names(graph, direction, lambda d: d in [3, 4]), - )) - - run_group_graphql_test(queries_and_expected_outputs, graph, sort_output=True) + threshold_str = "4" + threshold_float = 4.5 + queries_and_expected_outputs = [] + + for direction in ["BOTH", "IN", "OUT"]: + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f'degree: {{ direction: {direction}, where: {{ eq: {{ str: "{threshold_str}" }} }} }}', + _expected_degree_select_names( + graph, direction, lambda d: d == int(threshold_str) + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f'degree: {{ direction: {direction}, where: {{ eq: {{ str: "{threshold_str}" }} }} }}', + _expected_degree_names( + graph, direction, lambda d: d == int(threshold_str) + ), + ) + ) + + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ ge: {{ f64: {threshold_float} }} }} }}", + _expected_degree_select_names( + graph, direction, lambda d: d >= int(threshold_float) + ), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f"degree: {{ direction: {direction}, where: {{ ge: {{ f64: {threshold_float} }} }} }}", + _expected_degree_names( + graph, direction, lambda d: d >= int(threshold_float) + ), + ) + ) + + queries_and_expected_outputs.append( + _degree_select_nodes_query_expected_pair( + f'degree: {{ direction: {direction}, where: {{ isIn: {{ list: [{{str: "3"}}, {{f64: 4.9}}] }} }} }}', + _expected_degree_select_names(graph, direction, lambda d: d in [3, 4]), + ) + ) + queries_and_expected_outputs.append( + _degree_filter_nodes_query_expected_pair( + f'degree: {{ direction: {direction}, where: {{ isIn: {{ list: [{{str: "3"}}, {{f64: 4.9}}] }} }} }}', + _expected_degree_names(graph, direction, lambda d: d in [3, 4]), + ) + ) + + run_group_graphql_test(queries_and_expected_outputs, graph, sort_output=True) @pytest.mark.parametrize("graph", [EVENT_GRAPH, PERSISTENT_GRAPH]) def test_filter_nodes_degree_invalid_non_numeric_string_values_gql(graph): - invalid_exprs = [ - 'degree: { direction: BOTH, where: { lt: { str: "foo" } } }', - 'degree: { direction: IN, where: { eq: { str: "bar" } } }', - 'degree: { direction: OUT, where: { isIn: { list: [{str: "a"}, {str: "b"}] } } }', - 'degree: { direction: BOTH, where: { isNotIn: { list: [{str: "x"}, {str: "y"}] } } }', - ] - - for expr in invalid_exprs: - filter_nodes_query = f""" + invalid_exprs = [ + 'degree: { direction: BOTH, where: { lt: { str: "foo" } } }', + 'degree: { direction: IN, where: { eq: { str: "bar" } } }', + 'degree: { direction: OUT, where: { isIn: { list: [{str: "a"}, {str: "b"}] } } }', + 'degree: { direction: BOTH, where: { isNotIn: { list: [{str: "x"}, {str: "y"}] } } }', + ] + + for expr in invalid_exprs: + filter_nodes_query = f""" query {{ graph(path: "g") {{ filterNodes(expr: {{ {expr} }}) {{ @@ -428,7 +539,7 @@ def test_filter_nodes_degree_invalid_non_numeric_string_values_gql(graph): }} """ - select_nodes_query = f""" + select_nodes_query = f""" query {{ graph(path: "g") {{ nodes {{ @@ -440,30 +551,30 @@ def test_filter_nodes_degree_invalid_non_numeric_string_values_gql(graph): }} """ - run_graphql_error_test_contains(filter_nodes_query, ["Invalid filter"], graph) - run_graphql_error_test_contains(select_nodes_query, ["Invalid filter"], graph) + run_graphql_error_test_contains(filter_nodes_query, ["Invalid filter"], graph) + run_graphql_error_test_contains(select_nodes_query, ["Invalid filter"], graph) @pytest.mark.parametrize("graph", [EVENT_GRAPH, PERSISTENT_GRAPH]) def test_filter_nodes_degree_invalid_expressions_gql(graph): - invalid_exprs = [ - "degree: { direction: BOTH, where: { isNone: true } }", - "degree: { direction: IN, where: { isSome: true } }", - 'degree: { direction: OUT, where: { startsWith: { str: "1" } } }', - 'degree: { direction: BOTH, where: { endsWith: { str: "1" } } }', - 'degree: { direction: IN, where: { contains: { str: "1" } } }', - 'degree: { direction: OUT, where: { notContains: { str: "1" } } }', - "degree: { direction: BOTH, where: { any: { eq: { u64: 1 } } } }", - "degree: { direction: IN, where: { all: { eq: { u64: 1 } } } }", - "degree: { direction: OUT, where: { len: { gt: { u64: 0 } } } }", - "degree: { direction: BOTH, where: { sum: { eq: { u64: 1 } } } }", - "degree: { direction: IN, where: { avg: { eq: { u64: 1 } } } }", - "degree: { direction: OUT, where: { first: { eq: { u64: 1 } } } }", - "degree: { direction: BOTH, where: { last: { eq: { u64: 1 } } } }", - ] - - for expr in invalid_exprs: - filter_nodes_query = f""" + invalid_exprs = [ + "degree: { direction: BOTH, where: { isNone: true } }", + "degree: { direction: IN, where: { isSome: true } }", + 'degree: { direction: OUT, where: { startsWith: { str: "1" } } }', + 'degree: { direction: BOTH, where: { endsWith: { str: "1" } } }', + 'degree: { direction: IN, where: { contains: { str: "1" } } }', + 'degree: { direction: OUT, where: { notContains: { str: "1" } } }', + "degree: { direction: BOTH, where: { any: { eq: { u64: 1 } } } }", + "degree: { direction: IN, where: { all: { eq: { u64: 1 } } } }", + "degree: { direction: OUT, where: { len: { gt: { u64: 0 } } } }", + "degree: { direction: BOTH, where: { sum: { eq: { u64: 1 } } } }", + "degree: { direction: IN, where: { avg: { eq: { u64: 1 } } } }", + "degree: { direction: OUT, where: { first: { eq: { u64: 1 } } } }", + "degree: { direction: BOTH, where: { last: { eq: { u64: 1 } } } }", + ] + + for expr in invalid_exprs: + filter_nodes_query = f""" query {{ graph(path: "g") {{ filterNodes(expr: {{ {expr} }}) {{ @@ -475,7 +586,7 @@ def test_filter_nodes_degree_invalid_expressions_gql(graph): }} """ - select_nodes_query = f""" + select_nodes_query = f""" query {{ graph(path: "g") {{ nodes {{ @@ -487,11 +598,5 @@ def test_filter_nodes_degree_invalid_expressions_gql(graph): }} """ - run_graphql_error_test_contains(filter_nodes_query, ["Invalid filter"], graph) - run_graphql_error_test_contains(select_nodes_query, ["Invalid filter"], graph) - - - - - - + run_graphql_error_test_contains(filter_nodes_query, ["Invalid filter"], graph) + run_graphql_error_test_contains(select_nodes_query, ["Invalid filter"], graph) diff --git a/raphtory-graphql/schema.graphql b/raphtory-graphql/schema.graphql index ac83a98743..dfaf911edc 100644 --- a/raphtory-graphql/schema.graphql +++ b/raphtory-graphql/schema.graphql @@ -130,6 +130,32 @@ type CollectionOfNamespacedItem { count: Int! } +""" +Filters nodes by computed degree with a directional scope. + +`DegreeFilterNew` lets callers filter on: +- inbound degree (`IN`), +- outbound degree (`OUT`), +- or total degree (`BOTH`). + +The selected degree is compared using the `where` condition. + +Example (GraphQL): +```graphql +{ Degree: { direction: BOTH, where: { Gt: 10 } } } +``` +""" +enum DegreeDirection { + IN + OUT + BOTH +} + +input DegreeFilterNew { + direction: DegreeDirection! + where: PropCondition! +} + """ Document in a vector graph """ @@ -3623,6 +3649,10 @@ input NodeFilter @oneOf { """ property: PropertyFilterNew """ + Filters a node's degree (in, out, or total) by a condition. + """ + degree: DegreeFilterNew + """ Filters a node metadata field by name and condition. Metadata is shared across all temporal versions of a node. diff --git a/raphtory-graphql/src/lib.rs b/raphtory-graphql/src/lib.rs index 184f6a0049..1a3f2acfde 100644 --- a/raphtory-graphql/src/lib.rs +++ b/raphtory-graphql/src/lib.rs @@ -536,7 +536,6 @@ mod graphql_test { graph } - fn degree_graph_with_add_edge_only() -> Graph { let graph = Graph::new(); @@ -646,8 +645,6 @@ mod graphql_test { ); } - - #[tokio::test] async fn test_unique_temporal_properties() { let g = Graph::new(); diff --git a/raphtory-graphql/src/model/graph/filtering.rs b/raphtory-graphql/src/model/graph/filtering.rs index 3033016e1b..2ab6872c9c 100644 --- a/raphtory-graphql/src/model/graph/filtering.rs +++ b/raphtory-graphql/src/model/graph/filtering.rs @@ -1,4 +1,7 @@ -use crate::model::{graph::{node_id::GqlNodeId, property::Value, timeindex::GqlTimeInput}, plugins::operation}; +use crate::model::{ + graph::{node_id::GqlNodeId, property::Value, timeindex::GqlTimeInput}, + plugins::operation, +}; use async_graphql::dynamic::ValueAccessor; use dynamic_graphql::{ internal::{ @@ -7,16 +10,35 @@ use dynamic_graphql::{ Enum, InputObject, OneOfInput, }; use raphtory::{ - db::{api::{state::ops::Degree, view::internal::filtered_edge}, graph::views::filter::model::{ - ComposableFilter, DynFilter, DynView, NoFilter, ViewWrapOps, degree_filter::DegreeFilter, edge_filter::{CompositeEdgeFilter, EdgeFilter}, filter::{Filter, FilterValue}, filter_operator::FilterOperator, graph_filter::GraphFilter, is_active_edge_filter::IsActiveEdge, is_active_node_filter::IsActiveNode, is_deleted_filter::IsDeletedEdge, is_self_loop_filter::IsSelfLoopEdge, is_valid_filter::IsValidEdge, latest_filter::Latest as LatestWrap, layered_filter::Layered, node_filter::{CompositeNodeFilter, NodeFilter}, property_filter::{Op, PropertyFilter, PropertyFilterValue, PropertyRef}, snapshot_filter::{SnapshotAt as SnapshotAtWrap, SnapshotLatest as SnapshotLatestWrap}, windowed_filter::Windowed - }}, + db::{ + api::{state::ops::Degree, view::internal::filtered_edge}, + graph::views::filter::model::{ + degree_filter::DegreeFilter, + edge_filter::{CompositeEdgeFilter, EdgeFilter}, + filter::{Filter, FilterValue}, + filter_operator::FilterOperator, + graph_filter::GraphFilter, + is_active_edge_filter::IsActiveEdge, + is_active_node_filter::IsActiveNode, + is_deleted_filter::IsDeletedEdge, + is_self_loop_filter::IsSelfLoopEdge, + is_valid_filter::IsValidEdge, + latest_filter::Latest as LatestWrap, + layered_filter::Layered, + node_filter::{CompositeNodeFilter, NodeFilter}, + property_filter::{Op, PropertyFilter, PropertyFilterValue, PropertyRef}, + snapshot_filter::{SnapshotAt as SnapshotAtWrap, SnapshotLatest as SnapshotLatestWrap}, + windowed_filter::Windowed, + ComposableFilter, DynFilter, DynView, NoFilter, ViewWrapOps, + }, + }, errors::GraphError, }; -use raphtory_api::core::Direction; use raphtory_api::core::{ entities::{properties::prop::Prop, Layer, GID}, storage::timeindex::{AsTime, EventTime}, utils::time::IntoTime, + Direction, }; use serde::{Deserialize, Serialize}; use std::{ @@ -304,7 +326,6 @@ pub struct PropertyFilterNew { pub where_: PropCondition, } - /// Filters nodes by computed degree with a directional scope. /// /// `DegreeFilterNew` lets callers filter on: @@ -330,8 +351,8 @@ pub enum DegreeDirection { impl From for Direction { fn from(d: DegreeDirection) -> Self { match d { - DegreeDirection::In => Direction::IN, - DegreeDirection::Out => Direction::OUT, + DegreeDirection::In => Direction::IN, + DegreeDirection::Out => Direction::OUT, DegreeDirection::Both => Direction::BOTH, } } @@ -342,7 +363,7 @@ impl From for String { match d { DegreeDirection::In => "in_degree".to_string(), DegreeDirection::Out => "out_degree".to_string(), - DegreeDirection::Both => "degree".to_string(), + DegreeDirection::Both => "degree".to_string(), } } } @@ -1440,8 +1461,8 @@ impl TryFrom for CompositeNodeFilter { direction: core_direction, operator, value, - ops - })) + ops, + })) } GqlNodeFilter::Property(prop) => { let prop_ref = PropertyRef::Property(prop.name.clone()); diff --git a/raphtory-tests/tests/cached_view.rs b/raphtory-tests/tests/cached_view.rs deleted file mode 100644 index e9c132490a..0000000000 --- a/raphtory-tests/tests/cached_view.rs +++ /dev/null @@ -1,369 +0,0 @@ -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_tests::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_proptest() { - 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); - }) -} - -mod test_filters_cached_view { - use raphtory::{ - db::{ - api::view::StaticGraphViewOps, - graph::views::{cached_view::CachedView, window_graph::WindowedGraph}, - }, - prelude::{GraphViewOps, TimeOps}, - }; - use raphtory_tests::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_tests::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_tests::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-tests/tests/deletion_graph.rs b/raphtory-tests/tests/deletion_graph.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/raphtory-tests/tests/deletion_graph.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/raphtory-tests/tests/filter_tests/cached_view.rs b/raphtory-tests/tests/filter_tests/cached_view.rs new file mode 100644 index 0000000000..e77440ea5d --- /dev/null +++ b/raphtory-tests/tests/filter_tests/cached_view.rs @@ -0,0 +1,193 @@ +use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::{cached_view::CachedView, window_graph::WindowedGraph}, + }, + prelude::{GraphViewOps, TimeOps}, +}; +use raphtory_tests::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_tests::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, TestVariants, + }; + + use crate::filter_tests::cached_view::{ + CachedGraphTransformer, WindowedCachedGraphTransformer, + }; + + use crate::filter_tests::{init_graph, Edges, Nodes}; + + #[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( + |graph| init_graph(graph, Nodes::Typed, Edges::None), + CachedGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::None), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::None), + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::None), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::None), + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::None), + 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_tests::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestVariants, + }; + + use crate::filter_tests::cached_view::{ + CachedGraphTransformer, WindowedCachedGraphTransformer, + }; + use raphtory::db::graph::views::filter::model::{ + property_filter::ops::PropertyFilterOps, PropertyFilterFactory, + }; + + use crate::filter_tests::{init_graph, Edges, Nodes}; + + #[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( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + CachedGraphTransformer, + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + WindowedCachedGraphTransformer(6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + WindowedCachedGraphTransformer(6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } +} diff --git a/raphtory-tests/tests/edge_property_filter.rs b/raphtory-tests/tests/filter_tests/edge_property_filter.rs similarity index 100% rename from raphtory-tests/tests/edge_property_filter.rs rename to raphtory-tests/tests/filter_tests/edge_property_filter.rs diff --git a/raphtory-tests/tests/exploded_edge_property_filter.rs b/raphtory-tests/tests/filter_tests/exploded_edge_property_filter.rs similarity index 100% rename from raphtory-tests/tests/exploded_edge_property_filter.rs rename to raphtory-tests/tests/filter_tests/exploded_edge_property_filter.rs diff --git a/raphtory-tests/tests/filter_tests/mod.rs b/raphtory-tests/tests/filter_tests/mod.rs new file mode 100644 index 0000000000..34d697ad40 --- /dev/null +++ b/raphtory-tests/tests/filter_tests/mod.rs @@ -0,0 +1,89 @@ +use raphtory::{db::api::view::StaticGraphViewOps, prelude::*}; + +mod cached_view; +mod edge_property_filter; +mod node_property_filter; +mod subgraph_tests; +mod test_filters; +mod test_layers; +mod tests_node_type_filtered_subgraph; +mod views_test; + +/// Whether [`init_graph`] adds nodes, and whether they carry a node type. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Nodes { + /// Don't add any nodes. + None, + /// Add nodes without a node type. + Untyped, + /// Add nodes with their node type (`air_nomad` / `water_tribe` / `fire_nation`). + Typed, +} + +/// Whether [`init_graph`] adds edges, and whether they carry a layer. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Edges { + /// Don't add any edges. + None, + /// Add edges without a layer. + Unlayered, + /// Add edges with their layer (`layer1` / `layer2`). + Layered, +} + +/// Builds the filter-test graph in the variations the different filter test modules use +pub fn init_graph(graph: G, nodes: Nodes, edges: Edges) -> G { + if edges != Edges::None { + let edge_data = [ + (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, layer) in edge_data { + let layer = (edges == Edges::Layered).then_some(layer); + graph + .add_edge(ts, src, dst, [("p1", Prop::U64(p1))], layer) + .unwrap(); + } + } + + if nodes != Nodes::None { + let node_data = [ + (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, p1, node_type) in node_data { + let node_type = (nodes == Nodes::Typed).then_some(node_type); + graph + .add_node(ts, name, [("p1", Prop::U64(p1))], node_type, None) + .unwrap(); + } + } + + graph +} diff --git a/raphtory-tests/tests/node_property_filter.rs b/raphtory-tests/tests/filter_tests/node_property_filter.rs similarity index 100% rename from raphtory-tests/tests/node_property_filter.rs rename to raphtory-tests/tests/filter_tests/node_property_filter.rs diff --git a/raphtory-tests/tests/filter_tests/subgraph_tests.rs b/raphtory-tests/tests/filter_tests/subgraph_tests.rs new file mode 100644 index 0000000000..edd1dfa9e5 --- /dev/null +++ b/raphtory-tests/tests/filter_tests/subgraph_tests.rs @@ -0,0 +1,320 @@ +use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::{node_subgraph::NodeSubgraph, window_graph::WindowedGraph}, + }, + prelude::{GraphViewOps, NodeViewOps, TimeOps}, +}; +use raphtory_tests::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::filter_tests::subgraph_tests::{ + 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_tests::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, TestVariants, + }; + + use crate::filter_tests::{init_graph, Edges, Nodes}; + + #[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( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + NodeSubgraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + 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( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + NodeSubgraphTransformer(node_names.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + 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( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + 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( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + 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( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + 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( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Untyped, Edges::None), + 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_tests::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestVariants, + }; + + use crate::filter_tests::subgraph_tests::{ + NodeSubgraphTransformer, WindowedNodeSubgraphTransformer, + }; + + use crate::filter_tests::{init_graph, Edges, Nodes}; + + #[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( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + NodeSubgraphTransformer(None), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + NodeSubgraphTransformer(node_names.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + WindowedNodeSubgraphTransformer(None, 6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + WindowedNodeSubgraphTransformer(node_names.clone(), 6..9), + filter.clone(), + &expected_results, + vec![], + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Unlayered), + WindowedNodeSubgraphTransformer(node_names, 6..9), + filter, + &expected_results, + TestVariants::PersistentOnly, + ); + } +} diff --git a/raphtory-tests/tests/filter_tests/test_filters.rs b/raphtory-tests/tests/filter_tests/test_filters.rs new file mode 100644 index 0000000000..78d9bd8adf --- /dev/null +++ b/raphtory-tests/tests/filter_tests/test_filters.rs @@ -0,0 +1,13031 @@ +use raphtory::{db::api::view::StaticGraphViewOps, prelude::*}; + +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_tests::assertions::GraphTransformer; + +struct IdentityGraphTransformer; + +impl GraphTransformer for IdentityGraphTransformer { + type Return = G; + fn apply(&self, graph: G) -> Self::Return { + graph + } +} + +mod test_property_semantics { + mod test_node_property_filter_semantics { + use crate::filter_tests::test_filters::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_tests::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, + ); + } + } + + mod test_edge_property_filter_semantics { + use crate::filter_tests::test_filters::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_tests::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::filter_tests::test_filters::{ + init_nodes_graph, init_nodes_graph_with_num_ids, init_nodes_graph_with_str_ids, + IdentityGraphTransformer, + }; + use proptest::proptest; + use raphtory::{ + algorithms::alternating_mask::alternating_mask, + core::entities::VID, + db::{ + api::view::{filter_ops::NodeSelect, Filter}, + graph::views::filter::{ + model::{ + degree_filter::DegreeFilterFactory, + node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, + property_filter::ops::{ElemQualifierOps, ListAggOps, PropertyFilterOps}, + ComposableFilter, CompositeNodeFilter, NodeViewFilterOps, + PropertyFilterFactory, TryAsCompositeFilter, ViewWrapOps, + }, + CreateFilter, + }, + }, + errors::GraphError, + prelude::{ + AdditionOps, Graph, GraphViewOps, IntoProp, NodeFilter, NodeStateOps, NodeViewOps, + TimeOps, NO_PROPS, + }, + }; + use raphtory_api::core::{entities::properties::prop::Prop, Direction}; + use raphtory_tests::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, assert_select_nodes_results, + TestVariants, + }; + + fn sort_vids(mut vids: Vec) -> Vec { + vids.sort(); + vids + } + + fn candidates_with_history_after_filtering<'a, G: GraphViewOps<'a>>( + graph: &G, + candidate_nodes: Vec, + ) -> Vec { + let subgraph = graph.subgraph(candidate_nodes); + sort_vids( + subgraph + .nodes() + .into_iter() + .filter(|n| !n.history().is_empty()) + .map(|n| n.node) + .collect(), + ) + } + + fn assert_filter( + graph: &Graph, + filter: CF, + metric: Direction, + manual_expr: F, + context: &str, + ) where + CF: CreateFilter + TryAsCompositeFilter + Clone, + F: Fn(usize) -> bool + Copy, + { + let expected_select_nodes = graph + .nodes() + .into_iter() + .filter(|n| { + manual_expr(match metric { + Direction::BOTH => n.degree(), + Direction::IN => n.in_degree(), + Direction::OUT => n.out_degree(), + }) + }) + .map(|n| n.node) + .collect::>(); + + let expected_filter_nodes = + candidates_with_history_after_filtering(graph, expected_select_nodes.clone()); + + let filtered_event_graph = graph.filter(filter.clone()).unwrap(); + let filtered_event_nodes = sort_vids( + filtered_event_graph + .nodes() + .into_iter() + .map(|n| n.node) + .collect(), + ); + assert_eq!( + filtered_event_nodes, expected_filter_nodes, + "{} failed for event graph", + context + ); + + let selected_event_nodes = sort_vids( + graph + .nodes() + .select(filter.clone()) + .unwrap() + .into_iter() + .map(|n| n.node) + .collect(), + ); + assert_eq!( + selected_event_nodes, expected_select_nodes, + "{} failed for event graph select", + context + ); + + let filtered_persistent_graph = graph.persistent_graph().filter(filter.clone()).unwrap(); + let filtered_persistent_nodes = sort_vids( + filtered_persistent_graph + .nodes() + .into_iter() + .map(|n| n.node) + .collect(), + ); + assert_eq!( + filtered_persistent_nodes, expected_filter_nodes, + "{} failed for persistent graph", + context + ); + + let selected_persistent_nodes = sort_vids( + graph + .persistent_graph() + .nodes() + .select(filter) + .unwrap() + .into_iter() + .map(|n| n.node) + .collect(), + ); + assert_eq!( + selected_persistent_nodes, expected_select_nodes, + "{} failed for persistent graph select", + context + ); + } + + fn degree_graph_with_add_node_and_add_edge() -> Graph { + let graph = degree_graph_with_add_edge_only(); + let add_nodes = [ + (0, "1", Some("layer_a")), + (0, "7", None), + (0, "8", None), + (3, "9", Some("layer_a")), + (4, "9", Some("layer_c")), + (5, "10", Some("layer_b")), + (6, "10", Some("layer_e")), + (7, "11", Some("layer_d")), + (8, "12", Some("layer_f")), + (9, "12", Some("layer_c")), + ]; + for (t, id, layer) in add_nodes { + graph.add_node(t, id, NO_PROPS, None, layer).unwrap(); + } + graph + } + + fn degree_graph_with_add_edge_only() -> Graph { + let graph = Graph::new(); + + let edges = [ + (1, "1", "2", "layer_a"), + (1, "1", "3", "layer_b"), + (1, "1", "4", "layer_a"), + (1, "1", "5", "layer_b"), + (1, "1", "6", "layer_a"), + (2, "2", "1", "layer_b"), + (2, "2", "3", "layer_a"), + (2, "2", "4", "layer_b"), + (2, "2", "5", "layer_a"), + (3, "3", "1", "layer_a"), + (3, "3", "4", "layer_b"), + (3, "3", "5", "layer_a"), + (4, "4", "1", "layer_b"), + (4, "4", "2", "layer_a"), + (5, "5", "1", "layer_b"), + (6, "6", "1", "layer_a"), + (6, "4", "3", "layer_b"), + (6, "5", "2", "layer_a"), + (6, "6", "2", "layer_b"), + (6, "5", "3", "layer_a"), + (7, "2", "6", "layer_c"), + (7, "3", "6", "layer_d"), + (7, "6", "4", "layer_e"), + (7, "1", "5", "layer_f"), + (8, "3", "2", "layer_c"), + (8, "4", "6", "layer_d"), + (8, "2", "5", "layer_e"), + (8, "6", "3", "layer_f"), + (9, "5", "4", "layer_c"), + (9, "4", "5", "layer_d"), + (9, "2", "4", "layer_e"), + (9, "3", "1", "layer_f"), + ]; + for (t, src, dst, layer) in edges { + graph.add_edge(t, src, dst, NO_PROPS, Some(layer)).unwrap(); + } + + graph + } + + // Property-based tests for degree filtering + proptest! { + #[test] + fn prop_degree_filter_both_direction_comparison(threshold in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + + assert_filter( + &graph, + NodeFilter.degree().lt(threshold), + Direction::BOTH, + |d| d < threshold as usize, + &format!("BOTH < {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.degree().le(threshold), + Direction::BOTH, + |d| d <= threshold as usize, + &format!("BOTH <= {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.degree().eq(threshold), + Direction::BOTH, + |d| d == threshold as usize, + &format!("BOTH == {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.degree().ne(threshold), + Direction::BOTH, + |d| d != threshold as usize, + &format!("BOTH != {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.degree().ge(threshold), + Direction::BOTH, + |d| d >= threshold as usize, + &format!("BOTH >= {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.degree().gt(threshold), + Direction::BOTH, + |d| d > threshold as usize, + &format!("BOTH > {}", threshold), + ); + } + + #[test] + fn prop_degree_filter_in_direction_comparison(threshold in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + + assert_filter( + &graph, + NodeFilter.in_degree().lt(threshold), + Direction::IN, + |d| d < threshold as usize, + &format!("IN < {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.in_degree().le(threshold), + Direction::IN, + |d| d <= threshold as usize, + &format!("IN <= {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.in_degree().eq(threshold), + Direction::IN, + |d| d == threshold as usize, + &format!("IN == {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.in_degree().ne(threshold), + Direction::IN, + |d| d != threshold as usize, + &format!("IN != {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.in_degree().ge(threshold), + Direction::IN, + |d| d >= threshold as usize, + &format!("IN >= {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.in_degree().gt(threshold), + Direction::IN, + |d| d > threshold as usize, + &format!("IN > {}", threshold), + ); + } + + #[test] + fn prop_degree_filter_out_direction_comparison(threshold in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + + assert_filter( + &graph, + NodeFilter.out_degree().lt(threshold), + Direction::OUT, + |d| d < threshold as usize, + &format!("OUT < {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.out_degree().le(threshold), + Direction::OUT, + |d| d <= threshold as usize, + &format!("OUT <= {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.out_degree().eq(threshold), + Direction::OUT, + |d| d == threshold as usize, + &format!("OUT == {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.out_degree().ne(threshold), + Direction::OUT, + |d| d != threshold as usize, + &format!("OUT != {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.out_degree().ge(threshold), + Direction::OUT, + |d| d >= threshold as usize, + &format!("OUT >= {}", threshold), + ); + + assert_filter( + &graph, + NodeFilter.out_degree().gt(threshold), + Direction::OUT, + |d| d > threshold as usize, + &format!("OUT > {}", threshold), + ); + } + + #[test] + fn prop_degree_filter_and(threshold in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + + assert_filter( + &graph, + NodeFilter.degree().gt(threshold).and(NodeFilter.degree().lt(threshold + 5)), + Direction::BOTH, + |d| d > threshold as usize && d < (threshold + 5) as usize, + &format!("BOTH > {} AND BOTH < {}", threshold, threshold + 5), + ); + + assert_filter( + &graph, + NodeFilter.in_degree().gt(threshold).and(NodeFilter.in_degree().lt(threshold + 5)), + Direction::IN, + |d| d > threshold as usize && d < (threshold + 5) as usize, + &format!("IN > {} AND IN < {}", threshold, threshold + 5), + ); + + assert_filter( + &graph, + NodeFilter.out_degree().gt(threshold).and(NodeFilter.out_degree().lt(threshold + 5)), + Direction::OUT, + |d| d > threshold as usize && d < (threshold + 5) as usize, + &format!("OUT > {} AND OUT < {}", threshold, threshold + 5), + ); + } + + #[test] + fn prop_degree_filter_or(threshold in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + + assert_filter( + &graph, + NodeFilter.degree().lt(threshold).or(NodeFilter.degree().gt(threshold + 5)), + Direction::BOTH, + |d| d < threshold as usize || d > (threshold + 5) as usize, + &format!("BOTH < {} OR BOTH > {}", threshold, threshold + 5), + ); + + assert_filter( + &graph, + NodeFilter.in_degree().lt(threshold).or(NodeFilter.in_degree().gt(threshold + 5)), + Direction::IN, + |d| d < threshold as usize || d > (threshold + 5) as usize, + &format!("IN < {} OR IN > {}", threshold, threshold + 5), + ); + + assert_filter( + &graph, + NodeFilter.out_degree().lt(threshold).or(NodeFilter.out_degree().gt(threshold + 5)), + Direction::OUT, + |d| d < threshold as usize || d > (threshold + 5) as usize, + &format!("OUT < {} OR OUT > {}", threshold, threshold + 5), + ); + } + + #[test] + fn prop_degree_filter_not(threshold in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + + assert_filter( + &graph, + NodeFilter.degree().lt(threshold).or(NodeFilter.degree().gt(threshold + 5).not()), + Direction::BOTH, + |d| d < threshold as usize || d <= (threshold + 5) as usize, + &format!("BOTH < {} OR BOTH > {}", threshold, threshold + 5), + ); + + assert_filter( + &graph, + NodeFilter.in_degree().lt(threshold).or(NodeFilter.in_degree().gt(threshold + 5).not()), + Direction::IN, + |d| d < threshold as usize || d <= (threshold + 5) as usize, + &format!("IN < {} OR IN > {}", threshold, threshold + 5), + ); + + assert_filter( + &graph, + NodeFilter.out_degree().lt(threshold).or(NodeFilter.out_degree().gt(threshold + 5).not()), + Direction::OUT, + |d| d < threshold as usize || d <= (threshold + 5) as usize, + &format!("OUT < {} OR OUT > {}", threshold, threshold + 5), + ); + } + + #[test] + fn prop_degree_filter_is_in(val1 in 0u64..15, val2 in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + let set = [val1, val2]; + + assert_filter( + &graph, + NodeFilter.degree().is_in(vec![Prop::U64(val1), Prop::U64(val2)]), + Direction::BOTH, + |d| set.contains(&(d as u64)), + &format!("BOTH is_in({}, {})", val1, val2), + ); + + assert_filter( + &graph, + NodeFilter.in_degree().is_in(vec![Prop::U64(val1), Prop::U64(val2)]), + Direction::IN, + |d| set.contains(&(d as u64)), + &format!("IN is_in({}, {})", val1, val2), + ); + + assert_filter( + &graph, + NodeFilter.out_degree().is_in(vec![Prop::U64(val1), Prop::U64(val2)]), + Direction::OUT, + |d| set.contains(&(d as u64)), + &format!("OUT is_in({}, {})", val1, val2), + ); + } + + #[test] + fn prop_degree_filter_is_not_in(val1 in 0u64..15, val2 in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + let set = [val1, val2]; + + assert_filter( + &graph, + NodeFilter + .degree() + .is_not_in(vec![Prop::U64(val1), Prop::U64(val2)]), + Direction::BOTH, + |d| !set.contains(&(d as u64)), + &format!("BOTH is_not_in({}, {})", val1, val2), + ); + + assert_filter( + &graph, + NodeFilter + .in_degree() + .is_not_in(vec![Prop::U64(val1), Prop::U64(val2)]), + Direction::IN, + |d| !set.contains(&(d as u64)), + &format!("IN is_not_in({}, {})", val1, val2), + ); + + assert_filter( + &graph, + NodeFilter + .out_degree() + .is_not_in(vec![Prop::U64(val1), Prop::U64(val2)]), + Direction::OUT, + |d| !set.contains(&(d as u64)), + &format!("OUT is_not_in({}, {})", val1, val2), + ); + } + } + + #[test] + fn test_degree_filter_with_invalid_expressions() { + let graph = degree_graph_with_add_node_and_add_edge(); + let invalid_filters = vec![ + NodeFilter.degree().is_none(), + NodeFilter.degree().is_some(), + NodeFilter.degree().starts_with("1"), + NodeFilter.degree().ends_with("1"), + NodeFilter.degree().contains("1"), + NodeFilter.degree().not_contains("1"), + NodeFilter.degree().fuzzy_search("1", 1, false), + NodeFilter.in_degree().is_none(), + NodeFilter.in_degree().is_some(), + NodeFilter.in_degree().starts_with("1"), + NodeFilter.in_degree().ends_with("1"), + NodeFilter.in_degree().contains("1"), + NodeFilter.in_degree().not_contains("1"), + NodeFilter.in_degree().fuzzy_search("1", 1, false), + NodeFilter.out_degree().is_none(), + NodeFilter.out_degree().is_some(), + NodeFilter.out_degree().starts_with("1"), + NodeFilter.out_degree().ends_with("1"), + NodeFilter.out_degree().contains("1"), + NodeFilter.out_degree().not_contains("1"), + NodeFilter.out_degree().fuzzy_search("1", 1, false), + NodeFilter.degree().any().eq(1u64), + NodeFilter.degree().all().eq(1u64), + NodeFilter.degree().len().gt(0u64), + NodeFilter.degree().sum().eq(1u64), + NodeFilter.degree().avg().eq(1u64), + NodeFilter.degree().min().eq(1u64), + NodeFilter.degree().max().eq(1u64), + NodeFilter.degree().first().eq(1u64), + NodeFilter.degree().last().eq(1u64), + NodeFilter.in_degree().any().eq(1u64), + NodeFilter.in_degree().all().eq(1u64), + NodeFilter.in_degree().len().gt(0u64), + NodeFilter.in_degree().sum().eq(1u64), + NodeFilter.in_degree().avg().eq(1u64), + NodeFilter.in_degree().min().eq(1u64), + NodeFilter.in_degree().max().eq(1u64), + NodeFilter.in_degree().first().eq(1u64), + NodeFilter.in_degree().last().eq(1u64), + NodeFilter.out_degree().any().eq(1u64), + NodeFilter.out_degree().all().eq(1u64), + NodeFilter.out_degree().len().gt(0u64), + NodeFilter.out_degree().sum().eq(1u64), + NodeFilter.out_degree().avg().eq(1u64), + NodeFilter.out_degree().min().eq(1u64), + NodeFilter.out_degree().max().eq(1u64), + NodeFilter.out_degree().first().eq(1u64), + NodeFilter.out_degree().last().eq(1u64), + ]; + + for filter in invalid_filters { + assert!( + matches!(graph.filter(filter), Err(GraphError::InvalidFilter(_))), + "expected InvalidFilter for unsupported degree filter operation" + ); + } + } + + proptest! { + #[test] + fn prop_degree_filter_with_string_threshold(threshold in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + let threshold_str = threshold.to_string(); + let parsed_str = threshold_str.parse::().unwrap(); + + assert_filter(&graph, NodeFilter.degree().lt(threshold_str.clone()), Direction::BOTH, |d| d < parsed_str as usize, "BOTH < string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.degree().le(threshold_str.clone()), Direction::BOTH, |d| d <= parsed_str as usize, "BOTH <= string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.degree().eq(threshold_str.clone()), Direction::BOTH, |d| d == parsed_str as usize, "BOTH == string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.degree().ne(threshold_str.clone()), Direction::BOTH, |d| d != parsed_str as usize, "BOTH != string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.degree().ge(threshold_str.clone()), Direction::BOTH, |d| d >= parsed_str as usize, "BOTH >= string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.degree().gt(threshold_str.clone()), Direction::BOTH, |d| d > parsed_str as usize, "BOTH > string threshold parsed to u64"); + + assert_filter(&graph, NodeFilter.in_degree().lt(threshold_str.clone()), Direction::IN, |d| d < parsed_str as usize, "IN < string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.in_degree().le(threshold_str.clone()), Direction::IN, |d| d <= parsed_str as usize, "IN <= string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.in_degree().eq(threshold_str.clone()), Direction::IN, |d| d == parsed_str as usize, "IN == string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.in_degree().ne(threshold_str.clone()), Direction::IN, |d| d != parsed_str as usize, "IN != string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.in_degree().ge(threshold_str.clone()), Direction::IN, |d| d >= parsed_str as usize, "IN >= string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.in_degree().gt(threshold_str.clone()), Direction::IN, |d| d > parsed_str as usize, "IN > string threshold parsed to u64"); + + assert_filter(&graph, NodeFilter.out_degree().lt(threshold_str.clone()), Direction::OUT, |d| d < parsed_str as usize, "OUT < string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.out_degree().le(threshold_str.clone()), Direction::OUT, |d| d <= parsed_str as usize, "OUT <= string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.out_degree().eq(threshold_str.clone()), Direction::OUT, |d| d == parsed_str as usize, "OUT == string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.out_degree().ne(threshold_str.clone()), Direction::OUT, |d| d != parsed_str as usize, "OUT != string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.out_degree().ge(threshold_str.clone()), Direction::OUT, |d| d >= parsed_str as usize, "OUT >= string threshold parsed to u64"); + assert_filter(&graph, NodeFilter.out_degree().gt(threshold_str), Direction::OUT, |d| d > parsed_str as usize, "OUT > string threshold parsed to u64"); + } + + #[test] + fn prop_degree_filter_with_float_threshold(threshold in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + let threshold_float = threshold as f64; + let parsed_float = threshold_float as u64; + + assert_filter(&graph, NodeFilter.degree().lt(threshold_float), Direction::BOTH, |d| d < parsed_float as usize, "BOTH < float threshold cast to u64"); + assert_filter(&graph, NodeFilter.degree().le(threshold_float), Direction::BOTH, |d| d <= parsed_float as usize, "BOTH <= float threshold cast to u64"); + assert_filter(&graph, NodeFilter.degree().eq(threshold_float), Direction::BOTH, |d| d == parsed_float as usize, "BOTH == float threshold cast to u64"); + assert_filter(&graph, NodeFilter.degree().ne(threshold_float), Direction::BOTH, |d| d != parsed_float as usize, "BOTH != float threshold cast to u64"); + assert_filter(&graph, NodeFilter.degree().ge(threshold_float), Direction::BOTH, |d| d >= parsed_float as usize, "BOTH >= float threshold cast to u64"); + assert_filter(&graph, NodeFilter.degree().gt(threshold_float), Direction::BOTH, |d| d > parsed_float as usize, "BOTH > float threshold cast to u64"); + + assert_filter(&graph, NodeFilter.in_degree().lt(threshold_float), Direction::IN, |d| d < parsed_float as usize, "IN < float threshold cast to u64"); + assert_filter(&graph, NodeFilter.in_degree().le(threshold_float), Direction::IN, |d| d <= parsed_float as usize, "IN <= float threshold cast to u64"); + assert_filter(&graph, NodeFilter.in_degree().eq(threshold_float), Direction::IN, |d| d == parsed_float as usize, "IN == float threshold cast to u64"); + assert_filter(&graph, NodeFilter.in_degree().ne(threshold_float), Direction::IN, |d| d != parsed_float as usize, "IN != float threshold cast to u64"); + assert_filter(&graph, NodeFilter.in_degree().ge(threshold_float), Direction::IN, |d| d >= parsed_float as usize, "IN >= float threshold cast to u64"); + assert_filter(&graph, NodeFilter.in_degree().gt(threshold_float), Direction::IN, |d| d > parsed_float as usize, "IN > float threshold cast to u64"); + + assert_filter(&graph, NodeFilter.out_degree().lt(threshold_float), Direction::OUT, |d| d < parsed_float as usize, "OUT < float threshold cast to u64"); + assert_filter(&graph, NodeFilter.out_degree().le(threshold_float), Direction::OUT, |d| d <= parsed_float as usize, "OUT <= float threshold cast to u64"); + assert_filter(&graph, NodeFilter.out_degree().eq(threshold_float), Direction::OUT, |d| d == parsed_float as usize, "OUT == float threshold cast to u64"); + assert_filter(&graph, NodeFilter.out_degree().ne(threshold_float), Direction::OUT, |d| d != parsed_float as usize, "OUT != float threshold cast to u64"); + assert_filter(&graph, NodeFilter.out_degree().ge(threshold_float), Direction::OUT, |d| d >= parsed_float as usize, "OUT >= float threshold cast to u64"); + assert_filter(&graph, NodeFilter.out_degree().gt(threshold_float), Direction::OUT, |d| d > parsed_float as usize, "OUT > float threshold cast to u64"); + } + + #[test] + fn prop_degree_filter_with_string_is_in(threshold_a in 0u64..15, threshold_b in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + let threshold_a_str = threshold_a.to_string(); + let threshold_b_str = threshold_b.to_string(); + let parsed_a = threshold_a_str.parse::().unwrap(); + let parsed_b = threshold_b_str.parse::().unwrap(); + let set = [parsed_a, parsed_b]; + + assert_filter(&graph, NodeFilter.degree().is_in(vec![threshold_a_str.clone().into_prop(), threshold_b_str.clone().into_prop()]), Direction::BOTH, |d| set.contains(&(d as u64)), "BOTH is_in(string thresholds parsed to u64)"); + assert_filter(&graph, NodeFilter.in_degree().is_in(vec![threshold_a_str.clone().into_prop(), threshold_b_str.clone().into_prop()]), Direction::IN, |d| set.contains(&(d as u64)), "IN is_in(string thresholds parsed to u64)"); + assert_filter(&graph, NodeFilter.out_degree().is_in(vec![threshold_a_str.into_prop(), threshold_b_str.into_prop()]), Direction::OUT, |d| set.contains(&(d as u64)), "OUT is_in(string thresholds parsed to u64)"); + } + + #[test] + fn prop_degree_filter_with_string_is_not_in(threshold_a in 0u64..15, threshold_b in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + let threshold_a_str = threshold_a.to_string(); + let threshold_b_str = threshold_b.to_string(); + let parsed_a = threshold_a_str.parse::().unwrap(); + let parsed_b = threshold_b_str.parse::().unwrap(); + let set = [parsed_a, parsed_b]; + + assert_filter(&graph, NodeFilter.degree().is_not_in(vec![threshold_a_str.clone().into_prop(), threshold_b_str.clone().into_prop()]), Direction::BOTH, |d| !set.contains(&(d as u64)), "BOTH is_not_in(string thresholds parsed to u64)"); + assert_filter(&graph, NodeFilter.in_degree().is_not_in(vec![threshold_a_str.clone().into_prop(), threshold_b_str.clone().into_prop()]), Direction::IN, |d| !set.contains(&(d as u64)), "IN is_not_in(string thresholds parsed to u64)"); + assert_filter(&graph, NodeFilter.out_degree().is_not_in(vec![threshold_a_str.into_prop(), threshold_b_str.into_prop()]), Direction::OUT, |d| !set.contains(&(d as u64)), "OUT is_not_in(string thresholds parsed to u64)"); + } + + #[test] + fn prop_degree_filter_with_float_is_in(threshold_a in 0u64..15, threshold_b in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + let threshold_a_float = threshold_a as f64; + let threshold_b_float = threshold_b as f64; + let parsed_a = threshold_a_float as u64; + let parsed_b = threshold_b_float as u64; + let set = [parsed_a, parsed_b]; + + assert_filter(&graph, NodeFilter.degree().is_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::BOTH, |d| set.contains(&(d as u64)), "BOTH is_in(float thresholds cast to u64)"); + assert_filter(&graph, NodeFilter.in_degree().is_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::IN, |d| set.contains(&(d as u64)), "IN is_in(float thresholds cast to u64)"); + assert_filter(&graph, NodeFilter.out_degree().is_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::OUT, |d| set.contains(&(d as u64)), "OUT is_in(float thresholds cast to u64)"); + } + + #[test] + fn prop_degree_filter_with_float_is_not_in(threshold_a in 0u64..15, threshold_b in 0u64..15) { + let graph = degree_graph_with_add_node_and_add_edge(); + let threshold_a_float = threshold_a as f64; + let threshold_b_float = threshold_b as f64; + let parsed_a = threshold_a_float as u64; + let parsed_b = threshold_b_float as u64; + let set = [parsed_a, parsed_b]; + + assert_filter(&graph, NodeFilter.degree().is_not_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::BOTH, |d| !set.contains(&(d as u64)), "BOTH is_not_in(float thresholds cast to u64)"); + assert_filter(&graph, NodeFilter.in_degree().is_not_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::IN, |d| !set.contains(&(d as u64)), "IN is_not_in(float thresholds cast to u64)"); + assert_filter(&graph, NodeFilter.out_degree().is_not_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::OUT, |d| !set.contains(&(d as u64)), "OUT is_not_in(float thresholds cast to u64)"); + } + + #[test] + fn prop_degree_filter_invalid_non_numeric_string_values(value_a in "[a-zA-Z]{1,8}", value_b in "[a-zA-Z]{1,8}") { + let graph = degree_graph_with_add_node_and_add_edge(); + + let invalid_filters = vec![ + NodeFilter.degree().lt(value_a.clone()), + NodeFilter.degree().le(value_a.clone()), + NodeFilter.degree().eq(value_a.clone()), + NodeFilter.degree().ne(value_a.clone()), + NodeFilter.degree().ge(value_a.clone()), + NodeFilter.degree().gt(value_a.clone()), + NodeFilter.in_degree().lt(value_a.clone()), + NodeFilter.in_degree().le(value_a.clone()), + NodeFilter.in_degree().eq(value_a.clone()), + NodeFilter.in_degree().ne(value_a.clone()), + NodeFilter.in_degree().ge(value_a.clone()), + NodeFilter.in_degree().gt(value_a.clone()), + NodeFilter.out_degree().lt(value_a.clone()), + NodeFilter.out_degree().le(value_a.clone()), + NodeFilter.out_degree().eq(value_a.clone()), + NodeFilter.out_degree().ne(value_a.clone()), + NodeFilter.out_degree().ge(value_a.clone()), + NodeFilter.out_degree().gt(value_a.clone()), + NodeFilter.degree().is_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), + NodeFilter.degree().is_not_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), + NodeFilter.in_degree().is_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), + NodeFilter.in_degree().is_not_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), + NodeFilter.out_degree().is_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), + NodeFilter.out_degree().is_not_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), + ]; + + for filter in invalid_filters { + assert!( + matches!(graph.filter(filter), Err(GraphError::InvalidFilter(_))), + "expected InvalidFilter for non-numeric string values" + ); + } + } + } + + #[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, + ); + } +} + +mod test_node_property_filter { + use crate::filter_tests::test_filters::{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_tests::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, + ); + } +} + +mod test_node_composite_filter { + use raphtory_api::core::Direction; + + use crate::filter_tests::test_filters::{ + 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_tests::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, + ); + } +} + +mod test_node_property_filter_agg { + use crate::filter_tests::test_filters::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_tests::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()) + } + + /// Writes a set of node temporal properties and node metadata to the given graph. + pub fn init_nodes_graph< + G: StaticGraphViewOps + + AdditionOps + + InternalAdditionOps + + InternalPropertyAdditionOps + + PropertyAdditionOps, + >( + graph: G, + ) -> G { + // Each tuple represents (timestamp, node_name, properties). + 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(); + } + + // Each tuple represents (node_name, properties). + 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); + } +} + +mod test_edge_filter { + use crate::filter_tests::test_filters::{ + 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_tests::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, + ); + } +} + +mod test_edge_property_filter { + use crate::filter_tests::test_filters::{ + 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_tests::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, + ); + } +} + +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_tests::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, TestVariants, + }; + + use crate::filter_tests::test_filters::{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/tests/filter_tests/test_layers.rs b/raphtory-tests/tests/filter_tests/test_layers.rs new file mode 100644 index 0000000000..c0edccb61c --- /dev/null +++ b/raphtory-tests/tests/filter_tests/test_layers.rs @@ -0,0 +1,514 @@ +use raphtory::{ + db::{ + api::view::StaticGraphViewOps, + graph::views::{layer_graph::LayeredGraph, window_graph::WindowedGraph}, + }, + prelude::{LayerOps, TimeOps}, +}; +use raphtory_tests::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::filter_tests::test_layers::{ + LayeredGraphTransformer, LayeredGraphWindowTransformer, + }; + use raphtory::{db::graph::views::filter::model::PropertyFilterFactory, prelude::NodeFilter}; + + use crate::filter_tests::{init_graph, Edges, Nodes}; + use raphtory_tests::assertions::{ + assert_filter_nodes_results, assert_search_nodes_results, TestGraphVariants, TestVariants, + }; + + // 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::All, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::Graph], + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::PersistentOnly, + ); + assert_search_nodes_results( + |graph| init_graph(graph, Nodes::Typed, Edges::Layered), + 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_tests::assertions::{ + assert_filter_edges_results, assert_search_edges_results, TestGraphVariants, TestVariants, + }; + + use crate::filter_tests::test_layers::{ + LayeredGraphTransformer, LayeredGraphWindowTransformer, + }; + + use crate::filter_tests::{init_graph, Edges, Nodes}; + + #[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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphTransformer(layers.clone()), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + TestVariants::EventOnly, + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + 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( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphWindowTransformer(layers.clone(), 6..9), + filter.clone(), + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + assert_search_edges_results( + |graph| init_graph(graph, Nodes::None, Edges::Layered), + LayeredGraphWindowTransformer(layers, 6..9), + filter, + &expected_results, + vec![TestGraphVariants::PersistentGraph], + ); + } +} diff --git a/raphtory-tests/tests/tests_node_type_filtered_subgraph.rs b/raphtory-tests/tests/filter_tests/tests_node_type_filtered_subgraph.rs similarity index 92% rename from raphtory-tests/tests/tests_node_type_filtered_subgraph.rs rename to raphtory-tests/tests/filter_tests/tests_node_type_filtered_subgraph.rs index fb8190bb59..4bf7e5d878 100644 --- a/raphtory-tests/tests/tests_node_type_filtered_subgraph.rs +++ b/raphtory-tests/tests/filter_tests/tests_node_type_filtered_subgraph.rs @@ -219,35 +219,9 @@ mod test_filters_node_type_filtered_subgraph { }; 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::filter_tests::{init_graph, Edges, Nodes}; - use crate::test_filters_node_type_filtered_subgraph::{ + use crate::filter_tests::tests_node_type_filtered_subgraph::test_filters_node_type_filtered_subgraph::{ NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, }; use raphtory::{ @@ -262,14 +236,14 @@ mod test_filters_node_type_filtered_subgraph { let filter = NodeFilter.property("p1").eq(1u64); let expected_results = vec!["N1", "N3", "N4", "N6", "N7"]; assert_filter_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), NodeTypeGraphTransformer(None), filter.clone(), &expected_results, TestVariants::All, ); assert_search_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), NodeTypeGraphTransformer(None), filter, &expected_results, @@ -281,14 +255,14 @@ mod test_filters_node_type_filtered_subgraph { let filter = NodeFilter.property("p1").eq(1u64); let expected_results = vec!["N1", "N3", "N4", "N7"]; assert_filter_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), NodeTypeGraphTransformer(node_types.clone()), filter.clone(), &expected_results, TestVariants::All, ); assert_search_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), NodeTypeGraphTransformer(node_types), filter, &expected_results, @@ -302,14 +276,14 @@ mod test_filters_node_type_filtered_subgraph { let filter = NodeFilter.property("p1").eq(1u64); let expected_results = vec!["N1", "N3", "N6"]; assert_filter_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), WindowedNodeTypeGraphTransformer(None, 6..9), filter.clone(), &expected_results, vec![TestGraphVariants::Graph], ); assert_search_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), WindowedNodeTypeGraphTransformer(None, 6..9), filter, &expected_results, @@ -321,14 +295,14 @@ mod test_filters_node_type_filtered_subgraph { let filter = NodeFilter.property("p1").eq(1u64); let expected_results = vec!["N1", "N3"]; assert_filter_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), filter.clone(), &expected_results, vec![TestGraphVariants::Graph], ); assert_search_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), WindowedNodeTypeGraphTransformer(node_types, 6..9), filter, &expected_results, @@ -341,14 +315,14 @@ mod test_filters_node_type_filtered_subgraph { let filter = NodeFilter.property("p1").eq(1u64); let expected_results = vec!["N1", "N3", "N6", "N7"]; assert_filter_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), WindowedNodeTypeGraphTransformer(None, 6..9), filter.clone(), &expected_results, TestVariants::PersistentOnly, ); assert_search_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), WindowedNodeTypeGraphTransformer(None, 6..9), filter, &expected_results, @@ -360,14 +334,14 @@ mod test_filters_node_type_filtered_subgraph { let filter = NodeFilter.property("p1").eq(1u64); let expected_results = vec!["N1", "N3", "N7"]; assert_filter_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), WindowedNodeTypeGraphTransformer(node_types.clone(), 6..9), filter.clone(), &expected_results, TestVariants::PersistentOnly, ); assert_search_nodes_results( - init_graph, + |graph| init_graph(graph, Nodes::Typed, Edges::None), WindowedNodeTypeGraphTransformer(node_types, 6..9), filter, &expected_results, @@ -490,7 +464,7 @@ mod test_filters_node_type_filtered_subgraph { graph } - use crate::test_filters_node_type_filtered_subgraph::{ + use crate::filter_tests::tests_node_type_filtered_subgraph::test_filters_node_type_filtered_subgraph::{ LayeredNodeTypeGraphTransformer, LayeredWindowedNodeTypeGraphTransformer, NodeTypeGraphTransformer, WindowedNodeTypeGraphTransformer, }; diff --git a/raphtory-tests/tests/filter_tests/views_test.rs b/raphtory-tests/tests/filter_tests/views_test.rs new file mode 100644 index 0000000000..64e8c07b20 --- /dev/null +++ b/raphtory-tests/tests/filter_tests/views_test.rs @@ -0,0 +1,4032 @@ +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_tests::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_tests::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_tests::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-tests/tests/db_tests.rs b/raphtory-tests/tests/graph_tests/db_tests.rs similarity index 100% rename from raphtory-tests/tests/db_tests.rs rename to raphtory-tests/tests/graph_tests/db_tests.rs diff --git a/raphtory-tests/tests/graph_index.rs b/raphtory-tests/tests/graph_tests/graph_index.rs similarity index 100% rename from raphtory-tests/tests/graph_index.rs rename to raphtory-tests/tests/graph_tests/graph_index.rs diff --git a/raphtory-tests/tests/graph_tests/mod.rs b/raphtory-tests/tests/graph_tests/mod.rs new file mode 100644 index 0000000000..1d4c3fe498 --- /dev/null +++ b/raphtory-tests/tests/graph_tests/mod.rs @@ -0,0 +1,6 @@ +mod db_tests; +mod graph_index; +mod test_deletions; +mod test_history; +mod test_materialize; +mod valid_graph; diff --git a/raphtory-tests/tests/test_deletions.rs b/raphtory-tests/tests/graph_tests/test_deletions.rs similarity index 100% rename from raphtory-tests/tests/test_deletions.rs rename to raphtory-tests/tests/graph_tests/test_deletions.rs diff --git a/raphtory-tests/tests/test_history.rs b/raphtory-tests/tests/graph_tests/test_history.rs similarity index 100% rename from raphtory-tests/tests/test_history.rs rename to raphtory-tests/tests/graph_tests/test_history.rs diff --git a/raphtory-tests/tests/test_materialize.rs b/raphtory-tests/tests/graph_tests/test_materialize.rs similarity index 100% rename from raphtory-tests/tests/test_materialize.rs rename to raphtory-tests/tests/graph_tests/test_materialize.rs diff --git a/raphtory-tests/tests/valid_graph.rs b/raphtory-tests/tests/graph_tests/valid_graph.rs similarity index 100% rename from raphtory-tests/tests/valid_graph.rs rename to raphtory-tests/tests/graph_tests/valid_graph.rs diff --git a/raphtory-tests/tests/df_loaders.rs b/raphtory-tests/tests/io_tests/df_loaders.rs similarity index 100% rename from raphtory-tests/tests/df_loaders.rs rename to raphtory-tests/tests/io_tests/df_loaders.rs diff --git a/raphtory-tests/tests/io_tests/mod.rs b/raphtory-tests/tests/io_tests/mod.rs new file mode 100644 index 0000000000..2c43147925 --- /dev/null +++ b/raphtory-tests/tests/io_tests/mod.rs @@ -0,0 +1,4 @@ +mod df_loaders; +mod serialise_test; +mod test_materialize_sf10; +mod test_saved_graphs; diff --git a/raphtory-tests/tests/serialise_test.rs b/raphtory-tests/tests/io_tests/serialise_test.rs similarity index 100% rename from raphtory-tests/tests/serialise_test.rs rename to raphtory-tests/tests/io_tests/serialise_test.rs diff --git a/raphtory-tests/tests/test_materialize_sf10.rs b/raphtory-tests/tests/io_tests/test_materialize_sf10.rs similarity index 100% rename from raphtory-tests/tests/test_materialize_sf10.rs rename to raphtory-tests/tests/io_tests/test_materialize_sf10.rs diff --git a/raphtory-tests/tests/test_saved_graphs.rs b/raphtory-tests/tests/io_tests/test_saved_graphs.rs similarity index 100% rename from raphtory-tests/tests/test_saved_graphs.rs rename to raphtory-tests/tests/io_tests/test_saved_graphs.rs diff --git a/raphtory-tests/tests/node_edge_tests/mod.rs b/raphtory-tests/tests/node_edge_tests/mod.rs new file mode 100644 index 0000000000..9a6815fc81 --- /dev/null +++ b/raphtory-tests/tests/node_edge_tests/mod.rs @@ -0,0 +1,4 @@ +mod node_test; +mod test_edge; +mod test_edge_view; +mod test_exploded_edges; diff --git a/raphtory-tests/tests/node_test.rs b/raphtory-tests/tests/node_edge_tests/node_test.rs similarity index 100% rename from raphtory-tests/tests/node_test.rs rename to raphtory-tests/tests/node_edge_tests/node_test.rs diff --git a/raphtory-tests/tests/test_edge.rs b/raphtory-tests/tests/node_edge_tests/test_edge.rs similarity index 100% rename from raphtory-tests/tests/test_edge.rs rename to raphtory-tests/tests/node_edge_tests/test_edge.rs diff --git a/raphtory-tests/tests/test_edge_view.rs b/raphtory-tests/tests/node_edge_tests/test_edge_view.rs similarity index 100% rename from raphtory-tests/tests/test_edge_view.rs rename to raphtory-tests/tests/node_edge_tests/test_edge_view.rs diff --git a/raphtory-tests/tests/test_exploded_edges.rs b/raphtory-tests/tests/node_edge_tests/test_exploded_edges.rs similarity index 100% rename from raphtory-tests/tests/test_exploded_edges.rs rename to raphtory-tests/tests/node_edge_tests/test_exploded_edges.rs diff --git a/raphtory-tests/tests/subgraph_tests.rs b/raphtory-tests/tests/subgraph_tests.rs deleted file mode 100644 index 18ec426856..0000000000 --- a/raphtory-tests/tests/subgraph_tests.rs +++ /dev/null @@ -1,559 +0,0 @@ -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_tests::{ - test_storage, - 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_tests::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_tests::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_tests::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-tests/tests/test_filters.rs b/raphtory-tests/tests/test_filters.rs index a4bc92771c..328a67e88a 100644 --- a/raphtory-tests/tests/test_filters.rs +++ b/raphtory-tests/tests/test_filters.rs @@ -1,13024 +1 @@ -use raphtory::{db::api::view::StaticGraphViewOps, prelude::*}; - -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_tests::assertions::GraphTransformer; - -struct IdentityGraphTransformer; - -impl GraphTransformer for IdentityGraphTransformer { - type Return = G; - fn apply(&self, graph: G) -> Self::Return { - graph - } -} - -mod test_property_semantics { - 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_tests::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, - ); - } - } - - 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_tests::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, core::entities::VID, db::{ - api::view::{Filter, filter_ops::NodeSelect}, - graph::{ - views::filter::{ - CreateFilter, model::{ - ComposableFilter, CompositeNodeFilter, NodeViewFilterOps, PropertyFilterFactory, TryAsCompositeFilter, ViewWrapOps, degree_filter::DegreeFilterFactory, node_filter::ops::{NodeFilterOps, NodeIdFilterOps}, property_filter::ops::{ListAggOps, PropertyFilterOps} - } - }, - }, - }, errors::GraphError, prelude::{ - AdditionOps, Graph, GraphViewOps, NO_PROPS, NodeFilter, NodeStateOps, NodeViewOps, TimeOps - } - }; - use raphtory_tests::assertions::{ - assert_filter_nodes_results, assert_search_nodes_results, assert_select_nodes_results, - TestVariants, - }; - use raphtory_api::core::{Direction, entities::properties::prop::Prop}; - use raphtory::prelude::IntoProp; - use raphtory::db::graph::views::filter::model::property_filter::ops::ElemQualifierOps; - use proptest::proptest; - - fn sort_vids(mut vids: Vec) -> Vec { - vids.sort(); - vids - } - - fn candidates_with_history_after_filtering<'a, G: GraphViewOps<'a>>( - graph: &G, - candidate_nodes: Vec, - ) -> Vec { - let subgraph = graph.subgraph(candidate_nodes); - sort_vids( - subgraph - .nodes() - .into_iter() - .filter(|n| !n.history().is_empty()) - .map(|n| n.node) - .collect(), - ) - } - - fn assert_filter( - graph: &Graph, - filter: CF, - metric: Direction, - manual_expr: F, - context: &str, - ) where - CF: CreateFilter + TryAsCompositeFilter + Clone, - F: Fn(usize) -> bool + Copy, - { - let expected_select_nodes = graph - .nodes() - .into_iter() - .filter(|n| { - manual_expr(match metric { - Direction::BOTH => n.degree(), - Direction::IN => n.in_degree(), - Direction::OUT => n.out_degree(), - }) - }) - .map(|n| n.node) - .collect::>(); - - let expected_filter_nodes = candidates_with_history_after_filtering(graph, expected_select_nodes.clone()); - - let filtered_event_graph = graph.filter(filter.clone()).unwrap(); - let filtered_event_nodes = sort_vids( - filtered_event_graph - .nodes() - .into_iter() - .map(|n| n.node) - .collect(), - ); - assert_eq!( - filtered_event_nodes, expected_filter_nodes, - "{} failed for event graph", - context - ); - - let selected_event_nodes = sort_vids( - graph - .nodes() - .select(filter.clone()) - .unwrap() - .into_iter() - .map(|n| n.node) - .collect(), - ); - assert_eq!( - selected_event_nodes, expected_select_nodes, - "{} failed for event graph select", - context - ); - - let filtered_persistent_graph = graph.persistent_graph().filter(filter.clone()).unwrap(); - let filtered_persistent_nodes = sort_vids( - filtered_persistent_graph - .nodes() - .into_iter() - .map(|n| n.node) - .collect(), - ); - assert_eq!( - filtered_persistent_nodes, expected_filter_nodes, - "{} failed for persistent graph", - context - ); - - let selected_persistent_nodes = sort_vids( - graph - .persistent_graph() - .nodes() - .select(filter) - .unwrap() - .into_iter() - .map(|n| n.node) - .collect(), - ); - assert_eq!( - selected_persistent_nodes, expected_select_nodes, - "{} failed for persistent graph select", - context - ); - } - - fn degree_graph_with_add_node_and_add_edge() -> Graph { - let graph = degree_graph_with_add_edge_only(); - let add_nodes = [ - (0, "1", Some("layer_a")), - (0, "7", None), - (0, "8", None), - (3, "9", Some("layer_a")), - (4, "9", Some("layer_c")), - (5, "10", Some("layer_b")), - (6, "10", Some("layer_e")), - (7, "11", Some("layer_d")), - (8, "12", Some("layer_f")), - (9, "12", Some("layer_c")), - ]; - for (t, id, layer) in add_nodes { - graph.add_node(t, id, NO_PROPS, None, layer).unwrap(); - } - graph - } - - - fn degree_graph_with_add_edge_only() -> Graph { - let graph = Graph::new(); - - let edges = [ - (1, "1", "2", "layer_a"), - (1, "1", "3", "layer_b"), - (1, "1", "4", "layer_a"), - (1, "1", "5", "layer_b"), - (1, "1", "6", "layer_a"), - (2, "2", "1", "layer_b"), - (2, "2", "3", "layer_a"), - (2, "2", "4", "layer_b"), - (2, "2", "5", "layer_a"), - (3, "3", "1", "layer_a"), - (3, "3", "4", "layer_b"), - (3, "3", "5", "layer_a"), - (4, "4", "1", "layer_b"), - (4, "4", "2", "layer_a"), - (5, "5", "1", "layer_b"), - (6, "6", "1", "layer_a"), - (6, "4", "3", "layer_b"), - (6, "5", "2", "layer_a"), - (6, "6", "2", "layer_b"), - (6, "5", "3", "layer_a"), - (7, "2", "6", "layer_c"), - (7, "3", "6", "layer_d"), - (7, "6", "4", "layer_e"), - (7, "1", "5", "layer_f"), - (8, "3", "2", "layer_c"), - (8, "4", "6", "layer_d"), - (8, "2", "5", "layer_e"), - (8, "6", "3", "layer_f"), - (9, "5", "4", "layer_c"), - (9, "4", "5", "layer_d"), - (9, "2", "4", "layer_e"), - (9, "3", "1", "layer_f"), - ]; - for (t, src, dst, layer) in edges { - graph.add_edge(t, src, dst, NO_PROPS, Some(layer)).unwrap(); - } - - graph - } - - - // Property-based tests for degree filtering - proptest! { - #[test] - fn prop_degree_filter_both_direction_comparison(threshold in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - - assert_filter( - &graph, - NodeFilter.degree().lt(threshold), - Direction::BOTH, - |d| d < threshold as usize, - &format!("BOTH < {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.degree().le(threshold), - Direction::BOTH, - |d| d <= threshold as usize, - &format!("BOTH <= {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.degree().eq(threshold), - Direction::BOTH, - |d| d == threshold as usize, - &format!("BOTH == {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.degree().ne(threshold), - Direction::BOTH, - |d| d != threshold as usize, - &format!("BOTH != {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.degree().ge(threshold), - Direction::BOTH, - |d| d >= threshold as usize, - &format!("BOTH >= {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.degree().gt(threshold), - Direction::BOTH, - |d| d > threshold as usize, - &format!("BOTH > {}", threshold), - ); - } - - #[test] - fn prop_degree_filter_in_direction_comparison(threshold in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - - assert_filter( - &graph, - NodeFilter.in_degree().lt(threshold), - Direction::IN, - |d| d < threshold as usize, - &format!("IN < {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.in_degree().le(threshold), - Direction::IN, - |d| d <= threshold as usize, - &format!("IN <= {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.in_degree().eq(threshold), - Direction::IN, - |d| d == threshold as usize, - &format!("IN == {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.in_degree().ne(threshold), - Direction::IN, - |d| d != threshold as usize, - &format!("IN != {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.in_degree().ge(threshold), - Direction::IN, - |d| d >= threshold as usize, - &format!("IN >= {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.in_degree().gt(threshold), - Direction::IN, - |d| d > threshold as usize, - &format!("IN > {}", threshold), - ); - } - - #[test] - fn prop_degree_filter_out_direction_comparison(threshold in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - - assert_filter( - &graph, - NodeFilter.out_degree().lt(threshold), - Direction::OUT, - |d| d < threshold as usize, - &format!("OUT < {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.out_degree().le(threshold), - Direction::OUT, - |d| d <= threshold as usize, - &format!("OUT <= {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.out_degree().eq(threshold), - Direction::OUT, - |d| d == threshold as usize, - &format!("OUT == {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.out_degree().ne(threshold), - Direction::OUT, - |d| d != threshold as usize, - &format!("OUT != {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.out_degree().ge(threshold), - Direction::OUT, - |d| d >= threshold as usize, - &format!("OUT >= {}", threshold), - ); - - assert_filter( - &graph, - NodeFilter.out_degree().gt(threshold), - Direction::OUT, - |d| d > threshold as usize, - &format!("OUT > {}", threshold), - ); - } - - #[test] - fn prop_degree_filter_and(threshold in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - - assert_filter( - &graph, - NodeFilter.degree().gt(threshold).and(NodeFilter.degree().lt(threshold + 5)), - Direction::BOTH, - |d| d > threshold as usize && d < (threshold + 5) as usize, - &format!("BOTH > {} AND BOTH < {}", threshold, threshold + 5), - ); - - assert_filter( - &graph, - NodeFilter.in_degree().gt(threshold).and(NodeFilter.in_degree().lt(threshold + 5)), - Direction::IN, - |d| d > threshold as usize && d < (threshold + 5) as usize, - &format!("IN > {} AND IN < {}", threshold, threshold + 5), - ); - - assert_filter( - &graph, - NodeFilter.out_degree().gt(threshold).and(NodeFilter.out_degree().lt(threshold + 5)), - Direction::OUT, - |d| d > threshold as usize && d < (threshold + 5) as usize, - &format!("OUT > {} AND OUT < {}", threshold, threshold + 5), - ); - } - - #[test] - fn prop_degree_filter_or(threshold in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - - assert_filter( - &graph, - NodeFilter.degree().lt(threshold).or(NodeFilter.degree().gt(threshold + 5)), - Direction::BOTH, - |d| d < threshold as usize || d > (threshold + 5) as usize, - &format!("BOTH < {} OR BOTH > {}", threshold, threshold + 5), - ); - - assert_filter( - &graph, - NodeFilter.in_degree().lt(threshold).or(NodeFilter.in_degree().gt(threshold + 5)), - Direction::IN, - |d| d < threshold as usize || d > (threshold + 5) as usize, - &format!("IN < {} OR IN > {}", threshold, threshold + 5), - ); - - assert_filter( - &graph, - NodeFilter.out_degree().lt(threshold).or(NodeFilter.out_degree().gt(threshold + 5)), - Direction::OUT, - |d| d < threshold as usize || d > (threshold + 5) as usize, - &format!("OUT < {} OR OUT > {}", threshold, threshold + 5), - ); - } - - #[test] - fn prop_degree_filter_not(threshold in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - - assert_filter( - &graph, - NodeFilter.degree().lt(threshold).or(NodeFilter.degree().gt(threshold + 5).not()), - Direction::BOTH, - |d| d < threshold as usize || d <= (threshold + 5) as usize, - &format!("BOTH < {} OR BOTH > {}", threshold, threshold + 5), - ); - - assert_filter( - &graph, - NodeFilter.in_degree().lt(threshold).or(NodeFilter.in_degree().gt(threshold + 5).not()), - Direction::IN, - |d| d < threshold as usize || d <= (threshold + 5) as usize, - &format!("IN < {} OR IN > {}", threshold, threshold + 5), - ); - - assert_filter( - &graph, - NodeFilter.out_degree().lt(threshold).or(NodeFilter.out_degree().gt(threshold + 5).not()), - Direction::OUT, - |d| d < threshold as usize || d <= (threshold + 5) as usize, - &format!("OUT < {} OR OUT > {}", threshold, threshold + 5), - ); - } - - #[test] - fn prop_degree_filter_is_in(val1 in 0u64..15, val2 in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - let set = [val1, val2]; - - assert_filter( - &graph, - NodeFilter.degree().is_in(vec![Prop::U64(val1), Prop::U64(val2)]), - Direction::BOTH, - |d| set.contains(&(d as u64)), - &format!("BOTH is_in({}, {})", val1, val2), - ); - - assert_filter( - &graph, - NodeFilter.in_degree().is_in(vec![Prop::U64(val1), Prop::U64(val2)]), - Direction::IN, - |d| set.contains(&(d as u64)), - &format!("IN is_in({}, {})", val1, val2), - ); - - assert_filter( - &graph, - NodeFilter.out_degree().is_in(vec![Prop::U64(val1), Prop::U64(val2)]), - Direction::OUT, - |d| set.contains(&(d as u64)), - &format!("OUT is_in({}, {})", val1, val2), - ); - } - - #[test] - fn prop_degree_filter_is_not_in(val1 in 0u64..15, val2 in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - let set = [val1, val2]; - - assert_filter( - &graph, - NodeFilter - .degree() - .is_not_in(vec![Prop::U64(val1), Prop::U64(val2)]), - Direction::BOTH, - |d| !set.contains(&(d as u64)), - &format!("BOTH is_not_in({}, {})", val1, val2), - ); - - assert_filter( - &graph, - NodeFilter - .in_degree() - .is_not_in(vec![Prop::U64(val1), Prop::U64(val2)]), - Direction::IN, - |d| !set.contains(&(d as u64)), - &format!("IN is_not_in({}, {})", val1, val2), - ); - - assert_filter( - &graph, - NodeFilter - .out_degree() - .is_not_in(vec![Prop::U64(val1), Prop::U64(val2)]), - Direction::OUT, - |d| !set.contains(&(d as u64)), - &format!("OUT is_not_in({}, {})", val1, val2), - ); - } - } - - #[test] - fn test_degree_filter_with_invalid_expressions() { - let graph = degree_graph_with_add_node_and_add_edge(); - let invalid_filters = vec![ - NodeFilter.degree().is_none(), - NodeFilter.degree().is_some(), - NodeFilter.degree().starts_with("1"), - NodeFilter.degree().ends_with("1"), - NodeFilter.degree().contains("1"), - NodeFilter.degree().not_contains("1"), - NodeFilter.degree().fuzzy_search("1", 1, false), - NodeFilter.in_degree().is_none(), - NodeFilter.in_degree().is_some(), - NodeFilter.in_degree().starts_with("1"), - NodeFilter.in_degree().ends_with("1"), - NodeFilter.in_degree().contains("1"), - NodeFilter.in_degree().not_contains("1"), - NodeFilter.in_degree().fuzzy_search("1", 1, false), - NodeFilter.out_degree().is_none(), - NodeFilter.out_degree().is_some(), - NodeFilter.out_degree().starts_with("1"), - NodeFilter.out_degree().ends_with("1"), - NodeFilter.out_degree().contains("1"), - NodeFilter.out_degree().not_contains("1"), - NodeFilter.out_degree().fuzzy_search("1", 1, false), - NodeFilter.degree().any().eq(1u64), - NodeFilter.degree().all().eq(1u64), - NodeFilter.degree().len().gt(0u64), - NodeFilter.degree().sum().eq(1u64), - NodeFilter.degree().avg().eq(1u64), - NodeFilter.degree().min().eq(1u64), - NodeFilter.degree().max().eq(1u64), - NodeFilter.degree().first().eq(1u64), - NodeFilter.degree().last().eq(1u64), - NodeFilter.in_degree().any().eq(1u64), - NodeFilter.in_degree().all().eq(1u64), - NodeFilter.in_degree().len().gt(0u64), - NodeFilter.in_degree().sum().eq(1u64), - NodeFilter.in_degree().avg().eq(1u64), - NodeFilter.in_degree().min().eq(1u64), - NodeFilter.in_degree().max().eq(1u64), - NodeFilter.in_degree().first().eq(1u64), - NodeFilter.in_degree().last().eq(1u64), - NodeFilter.out_degree().any().eq(1u64), - NodeFilter.out_degree().all().eq(1u64), - NodeFilter.out_degree().len().gt(0u64), - NodeFilter.out_degree().sum().eq(1u64), - NodeFilter.out_degree().avg().eq(1u64), - NodeFilter.out_degree().min().eq(1u64), - NodeFilter.out_degree().max().eq(1u64), - NodeFilter.out_degree().first().eq(1u64), - NodeFilter.out_degree().last().eq(1u64), - ]; - - for filter in invalid_filters { - assert!( - matches!(graph.filter(filter), Err(GraphError::InvalidFilter(_))), - "expected InvalidFilter for unsupported degree filter operation" - ); - } - } - - proptest! { - #[test] - fn prop_degree_filter_with_string_threshold(threshold in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - let threshold_str = threshold.to_string(); - let parsed_str = threshold_str.parse::().unwrap(); - - assert_filter(&graph, NodeFilter.degree().lt(threshold_str.clone()), Direction::BOTH, |d| d < parsed_str as usize, "BOTH < string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.degree().le(threshold_str.clone()), Direction::BOTH, |d| d <= parsed_str as usize, "BOTH <= string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.degree().eq(threshold_str.clone()), Direction::BOTH, |d| d == parsed_str as usize, "BOTH == string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.degree().ne(threshold_str.clone()), Direction::BOTH, |d| d != parsed_str as usize, "BOTH != string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.degree().ge(threshold_str.clone()), Direction::BOTH, |d| d >= parsed_str as usize, "BOTH >= string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.degree().gt(threshold_str.clone()), Direction::BOTH, |d| d > parsed_str as usize, "BOTH > string threshold parsed to u64"); - - assert_filter(&graph, NodeFilter.in_degree().lt(threshold_str.clone()), Direction::IN, |d| d < parsed_str as usize, "IN < string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.in_degree().le(threshold_str.clone()), Direction::IN, |d| d <= parsed_str as usize, "IN <= string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.in_degree().eq(threshold_str.clone()), Direction::IN, |d| d == parsed_str as usize, "IN == string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.in_degree().ne(threshold_str.clone()), Direction::IN, |d| d != parsed_str as usize, "IN != string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.in_degree().ge(threshold_str.clone()), Direction::IN, |d| d >= parsed_str as usize, "IN >= string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.in_degree().gt(threshold_str.clone()), Direction::IN, |d| d > parsed_str as usize, "IN > string threshold parsed to u64"); - - assert_filter(&graph, NodeFilter.out_degree().lt(threshold_str.clone()), Direction::OUT, |d| d < parsed_str as usize, "OUT < string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.out_degree().le(threshold_str.clone()), Direction::OUT, |d| d <= parsed_str as usize, "OUT <= string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.out_degree().eq(threshold_str.clone()), Direction::OUT, |d| d == parsed_str as usize, "OUT == string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.out_degree().ne(threshold_str.clone()), Direction::OUT, |d| d != parsed_str as usize, "OUT != string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.out_degree().ge(threshold_str.clone()), Direction::OUT, |d| d >= parsed_str as usize, "OUT >= string threshold parsed to u64"); - assert_filter(&graph, NodeFilter.out_degree().gt(threshold_str), Direction::OUT, |d| d > parsed_str as usize, "OUT > string threshold parsed to u64"); - } - - #[test] - fn prop_degree_filter_with_float_threshold(threshold in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - let threshold_float = threshold as f64; - let parsed_float = threshold_float as u64; - - assert_filter(&graph, NodeFilter.degree().lt(threshold_float), Direction::BOTH, |d| d < parsed_float as usize, "BOTH < float threshold cast to u64"); - assert_filter(&graph, NodeFilter.degree().le(threshold_float), Direction::BOTH, |d| d <= parsed_float as usize, "BOTH <= float threshold cast to u64"); - assert_filter(&graph, NodeFilter.degree().eq(threshold_float), Direction::BOTH, |d| d == parsed_float as usize, "BOTH == float threshold cast to u64"); - assert_filter(&graph, NodeFilter.degree().ne(threshold_float), Direction::BOTH, |d| d != parsed_float as usize, "BOTH != float threshold cast to u64"); - assert_filter(&graph, NodeFilter.degree().ge(threshold_float), Direction::BOTH, |d| d >= parsed_float as usize, "BOTH >= float threshold cast to u64"); - assert_filter(&graph, NodeFilter.degree().gt(threshold_float), Direction::BOTH, |d| d > parsed_float as usize, "BOTH > float threshold cast to u64"); - - assert_filter(&graph, NodeFilter.in_degree().lt(threshold_float), Direction::IN, |d| d < parsed_float as usize, "IN < float threshold cast to u64"); - assert_filter(&graph, NodeFilter.in_degree().le(threshold_float), Direction::IN, |d| d <= parsed_float as usize, "IN <= float threshold cast to u64"); - assert_filter(&graph, NodeFilter.in_degree().eq(threshold_float), Direction::IN, |d| d == parsed_float as usize, "IN == float threshold cast to u64"); - assert_filter(&graph, NodeFilter.in_degree().ne(threshold_float), Direction::IN, |d| d != parsed_float as usize, "IN != float threshold cast to u64"); - assert_filter(&graph, NodeFilter.in_degree().ge(threshold_float), Direction::IN, |d| d >= parsed_float as usize, "IN >= float threshold cast to u64"); - assert_filter(&graph, NodeFilter.in_degree().gt(threshold_float), Direction::IN, |d| d > parsed_float as usize, "IN > float threshold cast to u64"); - - assert_filter(&graph, NodeFilter.out_degree().lt(threshold_float), Direction::OUT, |d| d < parsed_float as usize, "OUT < float threshold cast to u64"); - assert_filter(&graph, NodeFilter.out_degree().le(threshold_float), Direction::OUT, |d| d <= parsed_float as usize, "OUT <= float threshold cast to u64"); - assert_filter(&graph, NodeFilter.out_degree().eq(threshold_float), Direction::OUT, |d| d == parsed_float as usize, "OUT == float threshold cast to u64"); - assert_filter(&graph, NodeFilter.out_degree().ne(threshold_float), Direction::OUT, |d| d != parsed_float as usize, "OUT != float threshold cast to u64"); - assert_filter(&graph, NodeFilter.out_degree().ge(threshold_float), Direction::OUT, |d| d >= parsed_float as usize, "OUT >= float threshold cast to u64"); - assert_filter(&graph, NodeFilter.out_degree().gt(threshold_float), Direction::OUT, |d| d > parsed_float as usize, "OUT > float threshold cast to u64"); - } - - #[test] - fn prop_degree_filter_with_string_is_in(threshold_a in 0u64..15, threshold_b in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - let threshold_a_str = threshold_a.to_string(); - let threshold_b_str = threshold_b.to_string(); - let parsed_a = threshold_a_str.parse::().unwrap(); - let parsed_b = threshold_b_str.parse::().unwrap(); - let set = [parsed_a, parsed_b]; - - assert_filter(&graph, NodeFilter.degree().is_in(vec![threshold_a_str.clone().into_prop(), threshold_b_str.clone().into_prop()]), Direction::BOTH, |d| set.contains(&(d as u64)), "BOTH is_in(string thresholds parsed to u64)"); - assert_filter(&graph, NodeFilter.in_degree().is_in(vec![threshold_a_str.clone().into_prop(), threshold_b_str.clone().into_prop()]), Direction::IN, |d| set.contains(&(d as u64)), "IN is_in(string thresholds parsed to u64)"); - assert_filter(&graph, NodeFilter.out_degree().is_in(vec![threshold_a_str.into_prop(), threshold_b_str.into_prop()]), Direction::OUT, |d| set.contains(&(d as u64)), "OUT is_in(string thresholds parsed to u64)"); - } - - #[test] - fn prop_degree_filter_with_string_is_not_in(threshold_a in 0u64..15, threshold_b in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - let threshold_a_str = threshold_a.to_string(); - let threshold_b_str = threshold_b.to_string(); - let parsed_a = threshold_a_str.parse::().unwrap(); - let parsed_b = threshold_b_str.parse::().unwrap(); - let set = [parsed_a, parsed_b]; - - assert_filter(&graph, NodeFilter.degree().is_not_in(vec![threshold_a_str.clone().into_prop(), threshold_b_str.clone().into_prop()]), Direction::BOTH, |d| !set.contains(&(d as u64)), "BOTH is_not_in(string thresholds parsed to u64)"); - assert_filter(&graph, NodeFilter.in_degree().is_not_in(vec![threshold_a_str.clone().into_prop(), threshold_b_str.clone().into_prop()]), Direction::IN, |d| !set.contains(&(d as u64)), "IN is_not_in(string thresholds parsed to u64)"); - assert_filter(&graph, NodeFilter.out_degree().is_not_in(vec![threshold_a_str.into_prop(), threshold_b_str.into_prop()]), Direction::OUT, |d| !set.contains(&(d as u64)), "OUT is_not_in(string thresholds parsed to u64)"); - } - - #[test] - fn prop_degree_filter_with_float_is_in(threshold_a in 0u64..15, threshold_b in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - let threshold_a_float = threshold_a as f64; - let threshold_b_float = threshold_b as f64; - let parsed_a = threshold_a_float as u64; - let parsed_b = threshold_b_float as u64; - let set = [parsed_a, parsed_b]; - - assert_filter(&graph, NodeFilter.degree().is_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::BOTH, |d| set.contains(&(d as u64)), "BOTH is_in(float thresholds cast to u64)"); - assert_filter(&graph, NodeFilter.in_degree().is_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::IN, |d| set.contains(&(d as u64)), "IN is_in(float thresholds cast to u64)"); - assert_filter(&graph, NodeFilter.out_degree().is_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::OUT, |d| set.contains(&(d as u64)), "OUT is_in(float thresholds cast to u64)"); - } - - #[test] - fn prop_degree_filter_with_float_is_not_in(threshold_a in 0u64..15, threshold_b in 0u64..15) { - let graph = degree_graph_with_add_node_and_add_edge(); - let threshold_a_float = threshold_a as f64; - let threshold_b_float = threshold_b as f64; - let parsed_a = threshold_a_float as u64; - let parsed_b = threshold_b_float as u64; - let set = [parsed_a, parsed_b]; - - assert_filter(&graph, NodeFilter.degree().is_not_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::BOTH, |d| !set.contains(&(d as u64)), "BOTH is_not_in(float thresholds cast to u64)"); - assert_filter(&graph, NodeFilter.in_degree().is_not_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::IN, |d| !set.contains(&(d as u64)), "IN is_not_in(float thresholds cast to u64)"); - assert_filter(&graph, NodeFilter.out_degree().is_not_in(vec![threshold_a_float.into_prop(), threshold_b_float.into_prop()]), Direction::OUT, |d| !set.contains(&(d as u64)), "OUT is_not_in(float thresholds cast to u64)"); - } - - #[test] - fn prop_degree_filter_invalid_non_numeric_string_values(value_a in "[a-zA-Z]{1,8}", value_b in "[a-zA-Z]{1,8}") { - let graph = degree_graph_with_add_node_and_add_edge(); - - let invalid_filters = vec![ - NodeFilter.degree().lt(value_a.clone()), - NodeFilter.degree().le(value_a.clone()), - NodeFilter.degree().eq(value_a.clone()), - NodeFilter.degree().ne(value_a.clone()), - NodeFilter.degree().ge(value_a.clone()), - NodeFilter.degree().gt(value_a.clone()), - NodeFilter.in_degree().lt(value_a.clone()), - NodeFilter.in_degree().le(value_a.clone()), - NodeFilter.in_degree().eq(value_a.clone()), - NodeFilter.in_degree().ne(value_a.clone()), - NodeFilter.in_degree().ge(value_a.clone()), - NodeFilter.in_degree().gt(value_a.clone()), - NodeFilter.out_degree().lt(value_a.clone()), - NodeFilter.out_degree().le(value_a.clone()), - NodeFilter.out_degree().eq(value_a.clone()), - NodeFilter.out_degree().ne(value_a.clone()), - NodeFilter.out_degree().ge(value_a.clone()), - NodeFilter.out_degree().gt(value_a.clone()), - NodeFilter.degree().is_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), - NodeFilter.degree().is_not_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), - NodeFilter.in_degree().is_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), - NodeFilter.in_degree().is_not_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), - NodeFilter.out_degree().is_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), - NodeFilter.out_degree().is_not_in(vec![value_a.clone().into_prop(), value_b.clone().into_prop()]), - ]; - - for filter in invalid_filters { - assert!( - matches!(graph.filter(filter), Err(GraphError::InvalidFilter(_))), - "expected InvalidFilter for non-numeric string values" - ); - } - } - } - - #[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, - ); - } -} - -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_tests::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, - ); - } -} - -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_tests::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, - ); - } -} - -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_tests::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()) - } - - /// Writes a set of node temporal properties and node metadata to the given graph. - pub fn init_nodes_graph< - G: StaticGraphViewOps - + AdditionOps - + InternalAdditionOps - + InternalPropertyAdditionOps - + PropertyAdditionOps, - >( - graph: G, - ) -> G { - // Each tuple represents (timestamp, node_name, properties). - 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(); - } - - // Each tuple represents (node_name, properties). - 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); - } -} - -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_tests::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, - ); - } -} - -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_tests::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, - ); - } -} - -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_tests::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, - ); - } -} - +pub mod filter_tests; diff --git a/raphtory-tests/tests/test_graphs.rs b/raphtory-tests/tests/test_graphs.rs new file mode 100644 index 0000000000..8f1ba52c1f --- /dev/null +++ b/raphtory-tests/tests/test_graphs.rs @@ -0,0 +1 @@ +pub mod graph_tests; diff --git a/raphtory-tests/tests/test_io.rs b/raphtory-tests/tests/test_io.rs new file mode 100644 index 0000000000..265b89e50a --- /dev/null +++ b/raphtory-tests/tests/test_io.rs @@ -0,0 +1 @@ +pub mod io_tests; diff --git a/raphtory-tests/tests/test_layers.rs b/raphtory-tests/tests/test_layers.rs deleted file mode 100644 index 78b07db14b..0000000000 --- a/raphtory-tests/tests/test_layers.rs +++ /dev/null @@ -1,799 +0,0 @@ -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_tests::{ - test_storage, - utils::{build_graph, build_graph_layer, build_graph_strat, GraphFixture}, -}; -use serde_json::json; - -#[test] -fn proptest_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); -} - -// Regression for the build_graph_layer node-layer filter -#[test] -fn test_node_layer_visibility_under_valid_layers() { - let graph_f: GraphFixture = serde_json::from_value(json!({ - "nodes": { - "1": {"props":{"t_props":[[0,[]]],"c_props":[]}, "node_type": null, "node_layer": null}, - "2": {"props":{"t_props":[[0,[]]],"c_props":[]}, "node_type": null, "node_layer": "a"}, - "3": {"props":{"t_props":[[0,[]]],"c_props":[]}, "node_type": null, "node_layer": "b"} - }, - "edges": [] - })) - .unwrap(); - - let layer = ["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 proptest_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_tests::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_tests::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_tests::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-tests/tests/test_nodes_edges.rs b/raphtory-tests/tests/test_nodes_edges.rs new file mode 100644 index 0000000000..bc73567639 --- /dev/null +++ b/raphtory-tests/tests/test_nodes_edges.rs @@ -0,0 +1 @@ +pub mod node_edge_tests; diff --git a/raphtory-tests/tests/test_views.rs b/raphtory-tests/tests/test_views.rs new file mode 100644 index 0000000000..610211aa15 --- /dev/null +++ b/raphtory-tests/tests/test_views.rs @@ -0,0 +1 @@ +pub mod view_tests; diff --git a/raphtory-tests/tests/view_tests/cached_view.rs b/raphtory-tests/tests/view_tests/cached_view.rs new file mode 100644 index 0000000000..dab69c3817 --- /dev/null +++ b/raphtory-tests/tests/view_tests/cached_view.rs @@ -0,0 +1,122 @@ +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_tests::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_proptest() { + 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); + }) +} diff --git a/raphtory-tests/tests/view_tests/mod.rs b/raphtory-tests/tests/view_tests/mod.rs new file mode 100644 index 0000000000..91154462b9 --- /dev/null +++ b/raphtory-tests/tests/view_tests/mod.rs @@ -0,0 +1,5 @@ +mod cached_view; +mod subgraph_tests; +mod test_layers; +mod time_tests; +mod views_test; diff --git a/raphtory-tests/tests/view_tests/subgraph_tests.rs b/raphtory-tests/tests/view_tests/subgraph_tests.rs new file mode 100644 index 0000000000..1b75a8c162 --- /dev/null +++ b/raphtory-tests/tests/view_tests/subgraph_tests.rs @@ -0,0 +1,187 @@ +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_tests::{ + test_storage, + 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))] + ); +} + +#[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/tests/view_tests/test_layers.rs b/raphtory-tests/tests/view_tests/test_layers.rs new file mode 100644 index 0000000000..a34ae2e8b5 --- /dev/null +++ b/raphtory-tests/tests/view_tests/test_layers.rs @@ -0,0 +1,205 @@ +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_tests::{ + test_storage, + utils::{build_graph, build_graph_layer, build_graph_strat, GraphFixture}, +}; +use serde_json::json; + +#[test] +fn proptest_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); +} + +// Regression for the build_graph_layer node-layer filter +#[test] +fn test_node_layer_visibility_under_valid_layers() { + let graph_f: GraphFixture = serde_json::from_value(json!({ + "nodes": { + "1": {"props":{"t_props":[[0,[]]],"c_props":[]}, "node_type": null, "node_layer": null}, + "2": {"props":{"t_props":[[0,[]]],"c_props":[]}, "node_type": null, "node_layer": "a"}, + "3": {"props":{"t_props":[[0,[]]],"c_props":[]}, "node_type": null, "node_layer": "b"} + }, + "edges": [] + })) + .unwrap(); + + let layer = ["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 proptest_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")); + }); +} diff --git a/raphtory-tests/tests/time_tests.rs b/raphtory-tests/tests/view_tests/time_tests.rs similarity index 100% rename from raphtory-tests/tests/time_tests.rs rename to raphtory-tests/tests/view_tests/time_tests.rs diff --git a/raphtory-tests/tests/view_tests/views_test.rs b/raphtory-tests/tests/view_tests/views_test.rs new file mode 100644 index 0000000000..f5c167373d --- /dev/null +++ b/raphtory-tests/tests/view_tests/views_test.rs @@ -0,0 +1,532 @@ +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_tests::{test_storage, 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() { + 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() { + 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() { + 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() { + 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() { + 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],] + ); + }); +} diff --git a/raphtory-tests/tests/views_test.rs b/raphtory-tests/tests/views_test.rs deleted file mode 100644 index 435df284a8..0000000000 --- a/raphtory-tests/tests/views_test.rs +++ /dev/null @@ -1,4581 +0,0 @@ -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_tests::{test_storage, 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() { - 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() { - 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() { - 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() { - 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() { - 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_tests::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_tests::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_tests::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/src/db/api/state/ops/filter.rs b/raphtory/src/db/api/state/ops/filter.rs index 11c5a90747..25e4b70d47 100644 --- a/raphtory/src/db/api/state/ops/filter.rs +++ b/raphtory/src/db/api/state/ops/filter.rs @@ -2,18 +2,25 @@ use crate::{ db::{ api::{ state::{ - Index, ops::{Const, Degree, IntoDynNodeOp, NodeOp, TypeId} + ops::{Const, Degree, IntoDynNodeOp, NodeOp, TypeId}, + Index, }, view::internal::{GraphView, NodeList}, }, graph::{ create_node_type_filter, - views::filter::model::{FilterOperator, degree_filter::DegreeFilter, filter::{Filter, FilterValue}, node_filter::NodeFilter, property_filter::PropertyFilterValue}, + views::filter::model::{ + degree_filter::DegreeFilter, + filter::{Filter, FilterValue}, + node_filter::NodeFilter, + property_filter::PropertyFilterValue, + FilterOperator, + }, }, }, prelude::{GraphViewOps, PropertyFilter}, }; -use raphtory_api::core::entities::{VID, properties::prop::Prop}; +use raphtory_api::core::entities::{properties::prop::Prop, VID}; use raphtory_core::entities::nodes::node_ref::AsNodeRef; use raphtory_storage::graph::{graph::GraphStorage, nodes::node_storage_ops::NodeStorageOps}; use std::sync::Arc; @@ -225,19 +232,19 @@ impl NodeOp for NodePropertyFilterOp { pub struct NodeDegreeFilterOp { degree: Degree, operator: FilterOperator, - value: PropertyFilterValue + value: PropertyFilterValue, } impl NodeDegreeFilterOp { pub(crate) fn new(graph: G, filter: DegreeFilter) -> Self { let degree = Degree { dir: filter.direction, - view: graph + view: graph, }; Self { degree, operator: filter.operator, - value: filter.value + value: filter.value, } } } @@ -248,7 +255,8 @@ impl NodeOp for NodeDegreeFilterOp { fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output { let node_degree = self.degree.apply(storage, node); let node_degree_prop = Prop::U64(node_degree as u64); - self.operator.apply_to_property(&self.value, Some(&node_degree_prop)) + self.operator + .apply_to_property(&self.value, Some(&node_degree_prop)) } } diff --git a/raphtory/src/db/graph/views/filter/model/degree_filter.rs b/raphtory/src/db/graph/views/filter/model/degree_filter.rs index 18e5f3d463..ef6bc5c56f 100644 --- a/raphtory/src/db/graph/views/filter/model/degree_filter.rs +++ b/raphtory/src/db/graph/views/filter/model/degree_filter.rs @@ -1,24 +1,35 @@ -use std::collections::HashSet; -use std::sync::Arc; - -use raphtory_api::core::entities::properties::prop::PropType; -use raphtory_api::core::{Direction, entities::properties::prop::Prop}; -use raphtory_core::entities::{VID}; +use std::{collections::HashSet, sync::Arc}; + +use crate::{ + db::{ + api::{ + state::ops::{filter::NodeDegreeFilterOp, GraphView}, + view::{GraphViewOps, NodeViewOps}, + }, + graph::views::filter::{ + model, + model::{ + property_filter::{ + builders::{PropertyExprBuilder, PropertyExprBuilderInput}, + Op, PropertyFilter, PropertyFilterInput, PropertyFilterValue, PropertyRef, + }, + CombinedFilter, ComposableFilter, CompositeNodeFilter, EntityMarker, + FilterOperator, InternalPropertyFilterBuilder, NodeFilter, TryAsCompositeFilter, + }, + node_filtered_graph::NodeFilteredGraph, + CreateFilter, + }, + }, + errors::GraphError, +}; +use raphtory_api::core::{ + entities::properties::prop::{Prop, PropType}, + Direction, +}; +use raphtory_core::entities::VID; use raphtory_storage::graph::nodes::{node_ref::NodeStorageRef, node_storage_ops::NodeStorageOps}; -use crate::db::api::state::ops::GraphView; -use crate::db::api::state::ops::filter::NodeDegreeFilterOp; -use crate::db::graph::views::filter::CreateFilter; -use crate::db::graph::views::filter::model::{ComposableFilter, CompositeNodeFilter, NodeFilter}; -use crate::db::graph::views::filter::model::property_filter::{Op, PropertyFilterInput, PropertyRef, PropertyFilter}; -use crate::db::graph::views::filter::model::property_filter::builders::{PropertyExprBuilder, PropertyExprBuilderInput}; -use crate::db::graph::views::filter::model::{CombinedFilter, EntityMarker, InternalPropertyFilterBuilder, TryAsCompositeFilter}; -use crate::db::graph::views::filter::model; -use crate::db::graph::views::filter::node_filtered_graph::NodeFilteredGraph; -use crate::db::{api::view::{GraphViewOps, NodeViewOps}, graph::views::filter::model::{FilterOperator, property_filter::PropertyFilterValue}}; -use crate::errors::GraphError; use std::{fmt, fmt::Display}; - #[derive(Clone)] pub struct DegreeFilterBuilder { direction: Direction, @@ -42,7 +53,6 @@ pub struct DegreeFilter { pub ops: Vec, } - impl CreateFilter for DegreeFilter { type EntityFiltered<'graph, G: GraphViewOps<'graph>> = NodeFilteredGraph>; @@ -73,22 +83,30 @@ impl CreateFilter for DegreeFilter { )); } match self.operator { - FilterOperator::Eq | FilterOperator::Ne| FilterOperator::Gt | FilterOperator::Ge | FilterOperator::Lt | FilterOperator::Le | FilterOperator::IsIn | FilterOperator::IsNotIn => {}, + FilterOperator::Eq + | FilterOperator::Ne + | FilterOperator::Gt + | FilterOperator::Ge + | FilterOperator::Lt + | FilterOperator::Le + | FilterOperator::IsIn + | FilterOperator::IsNotIn => {} _ => { - return Err(GraphError::InvalidFilter( - format!("degree filter does not support operator {:?}", self.operator) - )); + return Err(GraphError::InvalidFilter(format!( + "degree filter does not support operator {:?}", + self.operator + ))); } } let value = match self.value { PropertyFilterValue::Single(ref prop_val) => { let casted_val = prop_val.clone().try_cast(PropType::U64).ok_or_else(|| { GraphError::InvalidFilter(format!( - "degree filter expects an integer value, got {}", + "degree filter expects an integer value, got {}", prop_val.to_string() )) })?; - + PropertyFilterValue::Single(casted_val) } PropertyFilterValue::Set(ref prop_vals) => { @@ -97,7 +115,7 @@ impl CreateFilter for DegreeFilter { .map(|val| { val.clone().try_cast(PropType::U64).ok_or_else(|| { GraphError::InvalidFilter(format!( - "degree filter expects an integer value, got {}", + "degree filter expects an integer value, got {}", val.to_string() )) }) @@ -108,11 +126,11 @@ impl CreateFilter for DegreeFilter { } PropertyFilterValue::None => { return Err(GraphError::InvalidFilter( - "degree filter requires a value".to_string() + "degree filter requires a value".to_string(), )); } - }; - let mut filter = self.clone(); + }; + let mut filter = self.clone(); filter.value = value; Ok(NodeDegreeFilterOp::new(graph, filter)) } @@ -126,19 +144,20 @@ impl CreateFilter for DegreeFilter { } impl TryAsCompositeFilter for DegreeFilter { - fn try_as_composite_edge_filter(&self) -> Result { - Err(GraphError::NotSupported) + fn try_as_composite_edge_filter( + &self, + ) -> Result { + Err(GraphError::NotSupported) } fn try_as_composite_exploded_edge_filter( &self, - ) -> Result - { - Err(GraphError::NotSupported) - } + ) -> Result { + Err(GraphError::NotSupported) + } fn try_as_composite_node_filter(&self) -> Result { Ok(CompositeNodeFilter::Degree(self.clone())) } -} +} fn property_ref(direction: &Direction) -> PropertyRef { match direction { @@ -150,7 +169,7 @@ fn property_ref(direction: &Direction) -> PropertyRef { impl InternalPropertyFilterBuilder for DegreeFilterBuilder where - DegreeFilter: CombinedFilter + DegreeFilter: CombinedFilter, { type Filter = DegreeFilter; type ExprBuilder = DegreeFilterBuilder; @@ -170,10 +189,10 @@ where fn filter(&self, filter: PropertyFilterInput) -> Self::Filter { DegreeFilter { - value: filter.prop_value, - direction: self.direction, - operator: filter.operator, - ops: filter.ops, + value: filter.prop_value, + direction: self.direction, + operator: filter.operator, + ops: filter.ops, } } @@ -187,7 +206,7 @@ where impl ComposableFilter for DegreeFilter {} pub trait DegreeFilterFactory { - fn in_degree(&self) -> DegreeFilterBuilder; + fn in_degree(&self) -> DegreeFilterBuilder; fn out_degree(&self) -> DegreeFilterBuilder; fn degree(&self) -> DegreeFilterBuilder; } @@ -203,4 +222,4 @@ impl Display for DegreeFilter { }; property_filter.fmt(f) } -} +} diff --git a/raphtory/src/db/graph/views/filter/model/mod.rs b/raphtory/src/db/graph/views/filter/model/mod.rs index ef20e4609e..b95369693f 100644 --- a/raphtory/src/db/graph/views/filter/model/mod.rs +++ b/raphtory/src/db/graph/views/filter/model/mod.rs @@ -56,6 +56,7 @@ use raphtory_api::core::{ use std::{ops::Deref, sync::Arc}; pub mod and_filter; +pub mod degree_filter; pub mod edge_filter; pub mod exploded_edge_filter; pub mod filter; @@ -75,8 +76,6 @@ pub mod or_filter; pub mod property_filter; pub mod snapshot_filter; pub mod windowed_filter; -pub mod degree_filter; - #[derive(Debug, Copy, Clone)] pub struct NoFilter; 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 4dd4cc38fd..85bf52f82e 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 @@ -1,31 +1,47 @@ use crate::{ + api::core::Direction, db::{ api::{ state::{ - NodeStateValue, TypedNodeState, ops::{ - NodeOp, TypeId, filter::{ + ops::{ + filter::{ AndOp, MaskOp, NodeIdFilterOp, NodeNameFilterOp, NodeTypeFilterOp, NotOp, OrOp, - } - } + }, + NodeOp, TypeId, + }, + NodeStateValue, TypedNodeState, }, - view::{BoxableGraphView, internal::GraphView}, + view::{internal::GraphView, BoxableGraphView}, }, graph::views::filter::{ - CreateFilter, model::{ - AndFilter, CombinedFilter, ComposableFilter, CompositeExplodedEdgeFilter, EntityMarker, InternalPropertyFilterFactory, InternalViewWrapOps, NodeViewFilterOps, NotFilter, OrFilter, TryAsCompositeFilter, Wrap, degree_filter::{DegreeFilter, DegreeFilterBuilder}, edge_filter::CompositeEdgeFilter, filter::Filter, is_active_node_filter::IsActiveNode, latest_filter::Latest, layered_filter::Layered, node_filter::{ + model::{ + degree_filter::{DegreeFilter, DegreeFilterBuilder, DegreeFilterFactory}, + edge_filter::CompositeEdgeFilter, + filter::Filter, + is_active_node_filter::IsActiveNode, + latest_filter::Latest, + layered_filter::Layered, + node_filter::{ builders::{NodeIdFilterBuilder, NodeNameFilterBuilder, NodeTypeFilterBuilder}, validate::validate, - }, node_state_filter::NodeStateBoolColOp, property_filter::builders::{MetadataFilterBuilder, PropertyFilterBuilder}, snapshot_filter::{SnapshotAt, SnapshotLatest}, windowed_filter::Windowed - }, node_filtered_graph::NodeFilteredGraph + }, + node_state_filter::NodeStateBoolColOp, + property_filter::builders::{MetadataFilterBuilder, PropertyFilterBuilder}, + snapshot_filter::{SnapshotAt, SnapshotLatest}, + windowed_filter::Windowed, + AndFilter, CombinedFilter, ComposableFilter, CompositeExplodedEdgeFilter, + EntityMarker, InternalPropertyFilterFactory, InternalViewWrapOps, + NodeViewFilterOps, NotFilter, OrFilter, TryAsCompositeFilter, Wrap, + }, + node_filtered_graph::NodeFilteredGraph, + CreateFilter, }, }, errors::GraphError, prelude::{GraphViewOps, PropertyFilter}, }; use raphtory_api::core::storage::timeindex::EventTime; -use crate::api::core::Direction; -use crate::db::graph::views::filter::model::degree_filter::DegreeFilterFactory; use std::{fmt, fmt::Display, sync::Arc}; pub mod builders; @@ -106,19 +122,18 @@ impl InternalPropertyFilterFactory for NodeFilter { impl DegreeFilterFactory for NodeFilter { fn degree(&self) -> DegreeFilterBuilder { - DegreeFilterBuilder::new(Direction::BOTH) + DegreeFilterBuilder::new(Direction::BOTH) } fn in_degree(&self) -> DegreeFilterBuilder { - DegreeFilterBuilder::new(Direction::IN) + DegreeFilterBuilder::new(Direction::IN) } fn out_degree(&self) -> DegreeFilterBuilder { - DegreeFilterBuilder::new(Direction::OUT) + DegreeFilterBuilder::new(Direction::OUT) } } - impl NodeViewFilterOps for NodeFilter { type Output = T; diff --git a/raphtory/src/python/filter/node_filter_builders.rs b/raphtory/src/python/filter/node_filter_builders.rs index 84317343c8..1fd4147f11 100644 --- a/raphtory/src/python/filter/node_filter_builders.rs +++ b/raphtory/src/python/filter/node_filter_builders.rs @@ -1,8 +1,14 @@ use crate::{ db::graph::views::filter::model::{ - NodeViewFilterOps, PropertyFilterFactory, ViewWrapOps, degree_filter::DegreeFilterFactory, node_filter::{ - NodeFilter, builders::{NodeIdFilterBuilder, NodeNameFilterBuilder, NodeTypeFilterBuilder}, ops::{NodeFilterOps, NodeIdFilterOps} - }, node_state_filter::NodeStateBoolColOp, property_filter::builders::{MetadataFilterBuilder, PropertyFilterBuilder} + degree_filter::DegreeFilterFactory, + node_filter::{ + builders::{NodeIdFilterBuilder, NodeNameFilterBuilder, NodeTypeFilterBuilder}, + ops::{NodeFilterOps, NodeIdFilterOps}, + NodeFilter, + }, + node_state_filter::NodeStateBoolColOp, + property_filter::builders::{MetadataFilterBuilder, PropertyFilterBuilder}, + NodeViewFilterOps, PropertyFilterFactory, ViewWrapOps, }, python::{ filter::{