-
Notifications
You must be signed in to change notification settings - Fork 51
Impelement get_homogeneous_pages. #87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
ff21c99
eff5d52
d37132b
df7466c
4843e68
9985e4e
bd486e1
e534243
5e6fd6a
f5075c3
9aae97f
cbefa30
bb86b7e
cdd0fc2
2af1ca2
76c2939
6867d83
19ff009
abdef8a
a5514cf
b7a34f5
2f4f4bd
3909554
3393641
f985063
e5feac2
46248d2
c5a606e
3ccdd3e
0e7cc0b
1378936
b610c69
d46719f
bdac930
1127c0f
24943ea
5bb4c5d
29985c4
87a6a08
993e45f
23ec6da
aff47d8
dfee9dd
403a397
ead71a3
685fca4
ccb3e91
4f62fba
43694f0
34c41b2
db47749
90e9ae7
8ecb334
6c33a0f
e230d1a
0917ceb
719440c
e192516
6b596a5
806ef9d
fa5fd49
1b9133b
1909b8e
1af24f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| from __future__ import annotations | ||
|
|
||
| from functools import partial | ||
| from dataclasses import dataclass | ||
| from typing import ( | ||
| Any, | ||
| List, | ||
|
|
@@ -438,3 +439,87 @@ def get_page( | |
| place, backwards = process_args(after, before, page) | ||
|
|
||
| return orm_get_page(query, per_page, place, backwards) | ||
|
|
||
|
|
||
| # Do we need to support python 3.6? | ||
|
||
| @dataclass | ||
| class PageRequest(Generic[_TP]): | ||
| """See ``get_page()`` documentation for parameter explanations.""" | ||
| query: Query[_TP] | ||
| per_page: int = PER_PAGE_DEFAULT | ||
| after: OptionalKeyset = None | ||
| before: OptionalKeyset = None | ||
| page: Optional[Union[MarkerLike, str]] = None | ||
|
|
||
|
|
||
| def get_homogeneous_page(queries: list[PageRequest[_TP]]) -> list[Page[Row[_TP]]]: | ||
|
||
| """Get multiple pages of results for homogeneous legacy ORM queries. | ||
|
|
||
| This only involves a single round trip to the database. To do that, under the | ||
| hood it generates a UNION ALL. That means each query must select exactly the | ||
| same columns. They may have different filters or ordering, but must result in | ||
| selecting the same columns with the same names. | ||
|
|
||
| Resulting pages are returned in the same order as the original page requests. | ||
| """ | ||
| if not queries: | ||
| return [] | ||
|
|
||
| paging_queries = [_prepare_homogeneous_page(query, i) for i, query in enumerate(queries)] | ||
|
|
||
| query = paging_queries[0].query.query | ||
| query = query.union_all(*[q.query.query for q in paging_queries[1:]]) | ||
|
|
||
| results = query.all() | ||
|
|
||
| # We need to make sure there's an entry for every page in case some return | ||
| # empty. | ||
| page_to_rows = {i: list() for i in range(len(queries))} | ||
| for row in results: | ||
| page_to_rows[row._page_identifier].append(row) | ||
|
|
||
| pages = [] | ||
| for i in range(len(queries)): | ||
| rows = page_to_rows[i] | ||
| pages.append(paging_queries[i].page_from_rows(rows)) | ||
|
||
|
|
||
|
|
||
| @dataclass | ||
| class _HomogeneousPagingQuery: | ||
| query: _PagingQuery | ||
| page_from_rows: Callable[[list[Row[_TP]]], Page[Row[_TP]]] | ||
|
|
||
|
|
||
| def _prepare_homogeneous_page( | ||
| query: PageRequest[_TP], page_identifier: int | ||
| ) -> _HomogeneousPagingQuery: | ||
| """page_identifier MUST be a trusted int, as it is not escaped.""" | ||
| # Should have no effect if called correctly, but let's be extra safe. | ||
| page_identifier = int(page_identifier) | ||
| place, backwards = process_args(query.after, query.before, query.page) | ||
|
|
||
| # Grab result_type and keys before adding the _page_identifier so that | ||
| # it isn't included in the results. | ||
| result_type = orm_result_type(query.query) | ||
|
||
| keys = orm_query_keys(query.query) | ||
| query.query = query.query.add_columns( | ||
|
||
| sa.sql.expression.literal_column(str(page_identifier)).label("_page_identifier") | ||
|
||
| ) | ||
|
|
||
| # Could we order by page identifier to do the page collation in the DB? | ||
|
|
||
| paging_query = prepare_paging( | ||
| q=query.query, | ||
| per_page=query.per_page, | ||
| place=place, | ||
| backwards=backwards, | ||
| orm=True, | ||
| dialect=query.query.session.get_bind().dialect, | ||
| ) | ||
|
|
||
| def page_from_rows(rows): | ||
| return orm_page_from_rows( | ||
| paging_query, rows, keys, result_type, per_page, backwards, current_place=place | ||
| ) | ||
|
|
||
| return _HomogeneousPagingQuery(query=paging_query, page_from_rows=page_from_rows) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CI fails at flake8 with a bunch of undefined names - looks like you've forgotten some imports?