Skip to content

Commit

Permalink
[resotocore][fix] Respect limit in with clause (#1292)
Browse files Browse the repository at this point in the history
  • Loading branch information
aquamatthias authored Nov 21, 2022
1 parent 9557ea7 commit bedaf8e
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 5 deletions.
15 changes: 10 additions & 5 deletions resotocore/resotocore/db/arango_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def filter_statement(current_cursor: str, part_term: Term, limit: Optional[Limit
for_stmt = f"LET {nested_distinct} = ({for_stmt} RETURN DISTINCT {crsr})"
crsr = next_crs()
sort_by = sort(crsr, p.sort) if p.sort else " "
for_stmt = f"{for_stmt} FOR {crsr} in {nested_distinct}{sort_by}{limited} "
for_stmt = f"{for_stmt} FOR {crsr} in {nested_distinct}{sort_by}{limited}"
else:
sort_by = sort(crsr, p.sort) if p.sort else " "
for_stmt = f"{for_stmt}{sort_by}{limited}"
Expand All @@ -350,7 +350,7 @@ def filter_statement(current_cursor: str, part_term: Term, limit: Optional[Limit
query_part += f"LET {filtered_out} = {reverse}({for_stmt}{return_stmt})"
return filtered_out

def with_clause(in_crsr: str, clause: WithClause) -> str:
def with_clause(in_crsr: str, clause: WithClause, limit: Optional[Limit]) -> str:
nonlocal query_part
# this is the general structure of the with_clause that is created
#
Expand Down Expand Up @@ -416,10 +416,12 @@ def collect_filter(cl: WithClause, depth: int) -> str:

out = next_crs()

limited = f" LIMIT {limit.offset}, {limit.length} " if limit else " "
query_part += (
f"LET {out} =( FOR {l0crsr} in {in_crsr} "
+ traversal_filter(clause, l0crsr, 1)
+ collect_filter(clause, 1)
+ limited
+ "RETURN l0_l0_res) "
)
return out
Expand Down Expand Up @@ -473,17 +475,20 @@ def navigation(in_crsr: str, nav: Navigation) -> str:
query_part += f"LET {nav_crsr} = UNION_DISTINCT({all_walks_combined})"
return nav_crsr

# apply the limit in the filter statement only, when no with clause is present
# otherwise the limit is applied in the with clause
filter_limit = p.limit if p.with_clause is None else None
if isinstance(p.term, MergeTerm):
# do not allow a limit in the prefilter
filter_cursor = filter_statement(in_cursor, p.term.pre_filter, None)
cursor, merge_part = merge(filter_cursor, p.term.merge)
query_part += merge_part
post = p.term.post_filter if p.term.post_filter else AllTerm()
# always do the post filter in case of sort or limit
cursor = filter_statement(cursor, post, p.limit)
cursor = filter_statement(cursor, post, filter_limit)
else:
cursor = filter_statement(in_cursor, p.term, p.limit)
cursor = with_clause(cursor, p.with_clause) if p.with_clause else cursor
cursor = filter_statement(in_cursor, p.term, filter_limit)
cursor = with_clause(cursor, p.with_clause, p.limit) if p.with_clause else cursor
cursor = navigation(cursor, p.navigation) if p.navigation else cursor
return p, cursor, filtered_out, query_part

Expand Down
9 changes: 9 additions & 0 deletions resotocore/tests/resotocore/db/arango_query_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,12 @@ def test_escape_property_path(foo_model: Model, graph_db: GraphDB) -> None:
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


def test_with_query_with_limit(foo_model: Model, graph_db: GraphDB) -> None:
query = "is(foo) with(empty, -->) limit 2"
query_str, _ = to_query(graph_db, QueryModel(parse_query(query), foo_model))
# make sure, there is no limit in the filter statement
assert "LET filter0 = (FOR m0 in ns FILTER @b0 IN m0.kinds RETURN m0)" in query_str
# make sure the limit is applied to the with statement
assert "FILTER counter1==1 LIMIT 0, 2 RETURN l0_l0_res" in query_str

0 comments on commit bedaf8e

Please sign in to comment.