async def test_query_aggregate(filled_graph_db: ArangoGraphDB, foo_model: Model) -> None: agg_query = parse_query( "aggregate(kind: count(identifier) as instances): is(foo)").on_section( "reported") async with await filled_graph_db.search_aggregation( QueryModel(agg_query, foo_model)) as gen: assert [x async for x in gen] == [{ "group": { "kind": "foo" }, "instances": 11 }] agg_combined_var_query = parse_query( 'aggregate("test_{kind}_{some_int}_{does_not_exist}" as kind: count(identifier) as instances): is("foo")' ).on_section("reported") async with await filled_graph_db.search_aggregation( QueryModel(agg_combined_var_query, foo_model)) as g: assert [x async for x in g] == [{ "group": { "kind": "test_foo_0_" }, "instances": 11 }] agg_multi_fn_same_prop = parse_query( 'aggregate(sum(f) as a, max(f) as b): is("bla")').on_section( "reported") async with await filled_graph_db.search_aggregation( QueryModel(agg_multi_fn_same_prop, foo_model)) as g: assert [x async for x in g] == [{"a": 2300, "b": 23}]
async def test_query_list(filled_graph_db: ArangoGraphDB, foo_model: Model) -> None: blas = Query.by("foo", P("identifier") == "9").traverse_out().filter( "bla", P("f") == 23) async with await filled_graph_db.search_list( QueryModel(blas.on_section("reported"), foo_model)) as gen: result = [from_js(x["reported"], Bla) async for x in gen] assert len(result) == 10 foos_or_blas = parse_query("is([foo, bla])") async with await filled_graph_db.search_list( QueryModel(foos_or_blas.on_section("reported"), foo_model)) as gen: result = [x async for x in gen] assert len(result) == 111 # 113 minus 1 graph_root, minus one cloud
def test_ancestors_kind_lookup(foo_model: Model, graph_db: GraphDB) -> None: # 1234 is coerced to a string query = "ancestors.account.reported.name==1234" assert to_query(graph_db, QueryModel(parse_query(query), foo_model))[1] == { "b0": "1234" }
async def test_query_not(filled_graph_db: ArangoGraphDB, foo_model: Model) -> None: # select everything that is not foo --> should be blas blas = Query.by(Query.mk_term("foo").not_term()) async with await filled_graph_db.search_list( QueryModel(blas.on_section("reported"), foo_model)) as gen: result = [from_js(x["reported"], Bla) async for x in gen] assert len(result) == 102
async def load_graph(db: GraphDB, model: Model, base_id: str = "sub_root") -> MultiDiGraph: blas = Query.by("foo", P("identifier") == base_id).traverse_out( 0, Navigation.Max) return await db.search_graph(QueryModel(blas.on_section("reported"), model))
def test_ip_range() -> None: bind_vars: Json = {} model = QueryModel( Query.by(IsTerm(["foo"])).on_section("reported"), Model.empty()) result = in_subnet( "crs", bind_vars, FunctionTerm("in_subnet", "foo.bla", ["192.168.1.0/24"]), model) assert result == "BIT_AND(IPV4_TO_NUMBER(crs.foo.bla), 4294967040) == @0" assert bind_vars["0"] == 3232235776
async def graph_query_model_from_request( self, request: Request) -> Tuple[GraphDB, QueryModel]: section = section_of(request) query_string = await request.text() graph_db = self.db.get_graph_db( request.match_info.get("graph_id", "resoto")) q = await self.query_parser.parse_query(query_string, section, **request.query) m = await self.model_handler.load_model() return graph_db, QueryModel(q, m)
def test_has_key() -> None: bind_vars: Json = {} model = QueryModel(Query.by("foo"), Model.empty()) result = has_key("crs", bind_vars, FunctionTerm("has_key", "foo.bla", [["a", "b", "c"]]), model) assert result == "@fn0 ALL IN ATTRIBUTES(crs.foo.bla, true)" assert bind_vars["fn0"] == ["a", "b", "c"] bind_vars2: Json = {} result = has_key("crs", bind_vars2, FunctionTerm("has_key", "foo.bla", ["a"]), model) assert result == "HAS(crs.foo.bla, @fn0)" assert bind_vars2["fn0"] == "a"
async def test_no_null_if_undefined(graph_db: ArangoGraphDB, foo_model: Model) -> None: await graph_db.wipe() # imported graph should not have any desired or metadata sections graph = create_graph("test", 0) for _, node in graph.nodes(True): del node["desired"] del node["metadata"] await graph_db.merge_graph(graph, foo_model) async with await graph_db.search_list( QueryModel(parse_query("all"), foo_model)) as cursor: async for elem in cursor: assert "reported" in elem assert "desired" not in elem assert "metadata" not in elem
async def test_query_graph(filled_graph_db: ArangoGraphDB, foo_model: Model) -> None: graph = await load_graph(filled_graph_db, foo_model) assert len(graph.edges) == 110 assert len(graph.nodes.values()) == 111 # filter data and tag result, and then traverse to the end of the graph in both directions around_me = Query.by( "foo", P("identifier") == "9").tag("red").traverse_inout(start=0) graph = await filled_graph_db.search_graph( QueryModel(around_me.on_section("reported"), foo_model)) assert len({x for x in graph.nodes}) == 12 assert GraphAccess.root_id(graph) == "sub_root" assert list(graph.successors("sub_root"))[0] == "9" assert set(graph.successors("9")) == {f"9_{x}" for x in range(0, 10)} for from_node, to_node, data in graph.edges.data(True): assert from_node == "9" or to_node == "9" assert data == {"edge_type": "default"} for node_id, node in graph.nodes.data(True): if node_id == "9": assert node["metadata"]["query_tag"] == "red" else: assert "tag" not in node["metadata"] async def assert_result(query: str, nodes: int, edges: int) -> None: q = parse_query(query) graph = await filled_graph_db.search_graph(QueryModel(q, foo_model)) assert len(graph.nodes) == nodes assert len(graph.edges) == edges await assert_result( "is(foo) and reported.identifier==9 <-delete[0:]default->", 11, 20) await assert_result( "is(foo) and reported.identifier==9 <-default[0:]delete->", 4, 3) await assert_result("is(foo) and reported.identifier==9 <-default[0:]->", 14, 13) await assert_result("is(foo) and reported.identifier==9 <-delete[0:]->", 11, 10) await assert_result("is(foo) and reported.identifier==9 -default[0:]->", 11, 10) await assert_result("is(foo) and reported.identifier==9 <-delete[0:]-", 11, 10) await assert_result("is(foo) and reported.identifier==9 <-default[0:]-", 4, 3) await assert_result("is(foo) and reported.identifier==9 -delete[0:]->", 1, 0)
async def test_query_merge(filled_graph_db: ArangoGraphDB, foo_model: Model) -> None: q = parse_query("is(foo) --> is(bla) { " "foo.bar.parents[]: <-[1:]-, " "foo.child: -->, " "walk: <-- -->, " "bla.agg: aggregate(sum(1) as count): <-[0:]- " "}") async with await filled_graph_db.search_list(QueryModel(q, foo_model), with_count=True) as cursor: assert cursor.count() == 100 async for bla in cursor: b = AccessJson(bla) assert b.reported.kind == "bla" assert len(b.foo.bar.parents) == 4 for parent in b.foo.bar.parents: assert parent.reported.kind in ["foo", "cloud", "graph_root"] assert b.walk.reported.kind == "bla" assert b.foo.child == AccessNone() assert b.bla.agg == [{"count": 5}]
async def test_query_with_merge(filled_graph_db: ArangoGraphDB, foo_model: Model) -> None: query = parse_query( '(merge_with_ancestors="foo as foobar,bar"): is("bla")') async with await filled_graph_db.search_list(QueryModel(query, foo_model) ) as cursor: async for bla in cursor: js = AccessJson(bla) assert "bar" in js.reported # key exists assert "bar" in js.desired # key exists assert "bar" in js.metadata # key exists assert js.reported.bar.is_none # bla is not a parent of this node assert js.desired.bar.is_none # bla is not a parent of this node assert js.metadata.bar.is_none # bla is not a parent of this node assert js.reported.foobar is not None # foobar is merged into reported assert js.desired.foobar is not None # foobar is merged into reported assert js.metadata.foobar is not None # foobar is merged into reported # make sure the correct parent is merged (foobar(1) -> bla(1_xxx)) assert js.reported.identifier.startswith( js.reported.foobar.identifier) assert js.reported.identifier.startswith(js.desired.foobar.node_id) assert js.reported.identifier.startswith( js.metadata.foobar.node_id)
async def query(q: str) -> List[Json]: agg_query = parse_query(q) async with await filled_graph_db.search_list( QueryModel(agg_query.on_section("reported"), foo_model)) as cursor: return [bla async for bla in cursor]
async def search(query: str) -> List[JsonElement]: async with await filled_graph_db.search_list( QueryModel(parse_query(query), foo_model)) as cursor: return [elem async for elem in cursor]
def check_sort_in_query(q: Query, expected_sort: str) -> None: query_str, _ = to_query(graph_db, QueryModel(q, foo_model)) assert f"SORT {expected_sort}" in query_str, f"Expected {expected_sort} in {query_str}"
async def assert_result(query: str, nodes: int, edges: int) -> None: q = parse_query(query) graph = await filled_graph_db.search_graph(QueryModel(q, foo_model)) assert len(graph.nodes) == nodes assert len(graph.edges) == edges
async def cost(query_str: str) -> EstimatedSearchCost: query = parse_query(query_str) return await query_cost(graph_db, QueryModel(query, foo_model), False)
def query_string(query: str) -> str: query_str, _ = to_query(graph_db, QueryModel(parse_query(query), foo_model)) return query_str
def test_escape_property_path(foo_model: Model, graph_db: GraphDB) -> None: raw = "metadata.replace.with.filter.sort.bla==true" query = to_query(graph_db, QueryModel(parse_query(raw), foo_model))[0] # aql keywords are escaped with backslashes assert "m0.metadata.`replace`.`with`.`filter`.`sort`.bla" in query